313 313
314Untuk setiap sesi yang menggunakan git yang dikelola Anthropic, runner juga mencatat baris `[runner:warn]` yang dimulai dengan `governed git: GIT_SSL_CAINFO is set` atau `governed git: GIT_SSL_NO_VERIFY is set`. Baris mengatakan apa yang dilakukan runner dengan variabel itu untuk git-nya sendiri, untuk git di dalam sesi, dan untuk hook lifecycle Anda. Itu berakhir dengan apakah Anda perlu mengubah apa pun.314Untuk setiap sesi yang menggunakan git yang dikelola Anthropic, runner juga mencatat baris `[runner:warn]` yang dimulai dengan `governed git: GIT_SSL_CAINFO is set` atau `governed git: GIT_SSL_NO_VERIFY is set`. Baris mengatakan apa yang dilakukan runner dengan variabel itu untuk git-nya sendiri, untuk git di dalam sesi, dan untuk hook lifecycle Anda. Itu berakhir dengan apakah Anda perlu mengubah apa pun.
315 315
316### Tambahkan repositori github.com ke sesi channel Claude Tag
317
318Jika Claude tidak dapat menambahkan repositori github.com ke sesi channel Claude Tag, pastikan channel tersebut memiliki akses ke repositori itu, bahkan ketika runner Anda dapat meng-clone-nya. Akses tersebut berasal dari pengaturan di GitHub dan di organisasi Claude Anda, bukan dari kredensial git runner Anda.
319
320**1. Instal Claude GitHub App**
321
322Instal [Claude GitHub App](https://github.com/apps/claude) pada organisasi atau akun GitHub yang memiliki repositori tersebut, dan sertakan repositori itu dalam instalasi.
323
324**2. Tautkan organisasi atau akun GitHub**
325
326Di [**Organization settings > Git providers**](https://claude.ai/admin-settings/source-control), tautkan organisasi atau akun tersebut ke organisasi Claude Anda. Owner organisasi Claude Anda yang juga memiliki organisasi GitHub dapat menautkannya. Lihat [Link your GitHub organization](https://claude.com/docs/claude-tag/admins/configure-github#link-your-github-organization).
327
328**3. Berikan akses repositori**
329
330Di [pengaturan admin Claude Tag](https://claude.ai/admin-settings/claude-tag), berikan akses repositori ke channel atau ke cakupan yang lebih luas yang mencakup channel tersebut: workspace Slack-nya atau seluruh Slack. Seseorang dengan peran Owner atau izin Claude Tag Admin dapat memberikannya. Pengelola channel juga dapat menambahkannya ke channel mereka sendiri jika akun GitHub mereka adalah admin repositori tersebut. Lihat [Grant repository access](https://claude.com/docs/claude-tag/admins/configure-github#grant-repository-access).
331
332Kemudian mulai thread baru di channel dan [sebutkan nama repositori](https://claude.com/docs/claude-tag/admins/configure-github#how-granted-repositories-reach-a-session) dalam pesan pertama Anda.
333
334Jika Claude masih tidak dapat menambahkan repositori, lihat [If Claude can't reach a repository](https://claude.com/docs/claude-tag/admins/configure-github#if-claude-can%E2%80%99t-reach-a-repository) untuk mengetahui di mana memeriksa tautan GitHub dan pemberian akses repositori.
335
316### Tulis ulang URL git untuk jaringan pribadi336### Tulis ulang URL git untuk jaringan pribadi
317 337
318URL repositori tiba dari bidang kontrol sebagai HTTPS, dengan nama host dari host git Anda; untuk GitHub Enterprise, itu adalah nama host yang Anda konfigurasi untuk [integrasi GitHub Enterprise](https://code.claude.com/docs/id/github-enterprise-server.md) di claude.ai. Dua flag yang dapat diulang menulis ulang URL tersebut sebelum klon:338URL repositori tiba dari bidang kontrol sebagai HTTPS, dengan nama host dari host git Anda; untuk GitHub Enterprise, itu adalah nama host yang Anda konfigurasi untuk [integrasi GitHub Enterprise](https://code.claude.com/docs/id/github-enterprise-server.md) di claude.ai. Dua flag yang dapat diulang menulis ulang URL tersebut sebelum klon:
376 396
377Build dan test biasanya merupakan bagian terbesar dan paling variabel dari beban sesi, jadi jalankan build representatif repositori Anda, ukur puncak CPU dan memorinya, dan naikkan nilai awal apa pun yang tidak meninggalkan ruang untuk proses Claude Code di atas puncak itu.397Build dan test biasanya merupakan bagian terbesar dan paling variabel dari beban sesi, jadi jalankan build representatif repositori Anda, ukur puncak CPU dan memorinya, dan naikkan nilai awal apa pun yang tidak meninggalkan ruang untuk proses Claude Code di atas puncak itu.
378 398
379Runner menggunakan `--capacity` untuk membatasi berapa banyak sesi yang dijalankannya sekaligus. Itu tidak membagi CPU atau memori di antara mereka, jadi sesi pada runner berbagi CPU dan memori kontainer. Untuk membatasi bagian satu sesi, terapkan batas dari [script wrapper](https://code.claude.com/docs/id/self-hosted-environments-configuration.md#wrapper-scripts) Anda. Apa yang harus diberikan satu kontainer oleh karena itu tergantung pada berapa banyak sesi yang dilayaninya sekaligus:399Runner menggunakan `--capacity` untuk membatasi berapa banyak sesi yang dijalankannya sekaligus. Itu tidak membagi CPU atau memori di antara mereka, jadi sesi pada runner berbagi CPU dan memori kontainer. Agar penggunaan memori satu sesi tidak mengakhiri sesi lainnya, jalankan satu sesi per runner dengan [batas memorinya sendiri](#give-each-session-its-own-memory-limit).
380 400
381* **Satu sesi per runner**: berikan setiap kontainer nilai satu sesi. Gunakan ukuran ini pada `--capacity 1`, yang [bagian pengerasan](#harden-your-deployment) rekomendasikan, dan untuk [runner on-demand](https://code.claude.com/docs/id/self-hosted-environments-configuration.md#on-demand-runners), di mana Anda menetapkan nilai pada workload yang [`spawn-runner` hook](https://code.claude.com/docs/id/self-hosted-environments-configuration#the-spawn-runner-hook) Anda kirimkan, seperti template pod Job Kubernetes.401* **Satu sesi per runner**: berikan setiap kontainer nilai satu sesi. Gunakan ukuran ini pada `--capacity 1`, yang [bagian pengerasan](#harden-your-deployment) rekomendasikan, dan untuk [runner on-demand](https://code.claude.com/docs/id/self-hosted-environments-configuration.md#on-demand-runners), di mana Anda menetapkan nilai pada workload yang [`spawn-runner` hook](https://code.claude.com/docs/id/self-hosted-environments-configuration#the-spawn-runner-hook) Anda kirimkan, seperti template pod Job Kubernetes.
382* **Beberapa sesi per runner**: pada `--capacity` di atas satu, kalikan nilai satu sesi dengan kapasitas, karena hingga banyak sesi dapat berjalan dalam kontainer sekaligus. Resep [Kubernetes](#kubernetes) dan [Docker Compose](#docker-compose) menjalankan `--capacity 4` tanpa batas CPU atau memori, jadi tambahkan batas yang diukur untuk kapasitas yang Anda jalankan.402* **Beberapa sesi per runner**: pada `--capacity` di atas satu, kalikan nilai satu sesi dengan kapasitas, karena hingga banyak sesi dapat berjalan dalam kontainer sekaligus. Resep [Kubernetes](#kubernetes) dan [Docker Compose](#docker-compose) menjalankan `--capacity 4` tanpa batas CPU atau memori, jadi tambahkan batas yang diukur untuk kapasitas yang Anda jalankan.
403* **Beberapa kontainer runner pada satu host**: ukur host untuk jumlah kapasitas semua runner, karena setiap runner hanya menghitung sesinya sendiri terhadap `--capacity`:
404
405 * **Memori**: kalikan memori satu sesi dengan jumlah tersebut, lalu tambahkan apa yang digunakan oleh proses host itu sendiri
406 * **CPU**: kalikan permintaan CPU satu sesi dengan jumlah tersebut. Agar setiap sesi dapat meledak sekaligus, kalikan batas CPU-nya sebagai gantinya.
407
408 Berikan setiap runner kontainernya sendiri. Gunakan [path `--base-dir` yang sama](#keep-the-base-directory-and-capacity-identical-across-runners) di masing-masing, pada penyimpanan yang tidak di-mount oleh kontainer runner lain, sehingga setiap runner memiliki [direktori kerjanya sendiri](#harden-your-deployment).
409
410### Berikan setiap sesi batas memorinya sendiri
411
412Untuk mencegah satu sesi mengakhiri sesi lainnya ketika memori habis, jalankan setiap runner pada `--capacity 1` di bawah batas memorinya sendiri, yang diatur ke memori satu sesi:
413
414* **Di dalam kontainer**: jalankan satu runner per kontainer, dan atur batas memori kontainer, seperti `limits.memory` di Kubernetes, `--memory` di Docker, atau `mem_limit` dalam file Compose. Jaga agar jumlah batas semua kontainer tetap di bawah memori host, karena ketika host itu sendiri kehabisan memori, kernel dapat membunuh proses di mana saja pada host.
415* **Di bawah systemd tanpa kontainer**: atur `MemoryMax` pada unit runner, dan jalankan satu runner per host, karena runner yang [menggunakan `--base-dir` yang sama](#keep-the-base-directory-and-capacity-identical-across-runners) pada satu sistem file berbagi repositori yang di-clone di sana. Misalnya, pada `--capacity 1` tanpa hook `checkout`, sesi mereka pada satu repositori dapat bekerja di checkout yang sama sekaligus. Untuk menjalankan beberapa runner pada satu host, berikan masing-masing sebuah kontainer.
416
417Ketika kontainer atau unit mencapai batas memorinya, kernel hanya membunuh proses di dalamnya. Jika Anda mengatur `--capacity` di atas satu, sesi-sesi tersebut berbagi satu batas. Ketika mereka mencapainya, kernel tidak memilih berdasarkan sesi proses mana yang akan dibunuh, sehingga penggunaan memori satu sesi dapat mengakhiri sesi lainnya.
418
419Di Kubernetes, Anda tidak dapat memisahkan sesi dengan nilai mana pun dari pengaturan `singleProcessOOMKill` milik kubelet. Anda juga tidak dapat membatasi total memori sesi dengan `ulimit` memori dalam [script wrapper](https://code.claude.com/docs/id/self-hosted-environments-configuration.md#wrapper-scripts) Anda. Kernel menerapkan nilai seperti `ulimit -v` ke setiap proses secara terpisah.
420
421### Sisakan ruang di bawah batas proses dan thread
422
423Untuk menentukan ukuran batas proses dan thread, ukur apa yang dibutuhkan satu sesi dan kalikan dengan jumlah sesi yang berbagi batas tersebut. Ketika sesi-sesi tersebut mencapai batas, kernel menolak proses dan thread baru, sehingga perintah, build, dan test di salah satu sesi dapat gagal. Panggilan yang ditolak mengembalikan `EAGAIN`, yang pesan standarnya adalah `Resource temporarily unavailable`.
424
425Misalkan satu sesi mencapai puncak 300 proses dan thread, dan empat sesi berbagi batas tersebut. Bersama-sama mereka membutuhkan 1.200, dan dengan batas 2.400, Anda menyisakan ruang untuk build yang membutuhkan dua kali lipat.
426
427#### Tempat mengatur setiap batas
428
429Setiap batas dalam tabel ini menghitung thread maupun proses, dan `pids.max` adalah file yang menyimpan batas cgroup:
430
431| Batas | Tempat Anda mengaturnya | Apa yang berbagi batas tersebut |
432| :- | :- | :- |
433| `pids.max` di Kubernetes | `podPidsLimit` dalam konfigurasi kubelet dari node yang menjalankan runner. Anda tidak dapat mengaturnya dalam spec Pod. | Kontainer-kontainer dari satu Pod |
434| `pids.max` di Docker | `--pids-limit`, atau `pids_limit` dalam file Compose | Satu kontainer |
435| `pids.max` di bawah systemd | `TasksMax` pada unit runner | Satu unit |
436| `ulimit -u` | Shell atau layanan yang memulai runner | Semua hal pada host yang berjalan sebagai user ID runner. Tidak diberlakukan untuk root. |
437
438Ketika Anda tidak mengatur `TasksMax`, systemd menerapkan `DefaultTasksMax`, yang secara default bernilai 15% dari nilai terendah di antara `kernel.pid_max`, `kernel.threads-max`, dan `pids.max` milik cgroup root.
439
440#### Ukur puncak satu sesi
441
442Untuk mendapatkan angka bagi runner Anda sendiri, ukur satu sesi pada host-nya, yang di Kubernetes adalah node:
443
444**1. Mulai satu runner sendirian**
445
446Mulai satu runner pada `--capacity 1` tanpa hal lain di kontainer atau unit systemd-nya, sendirian pada host-nya dan di environment-nya:
447
448* **Host-nya**: gunakan host yang tidak menjalankan runner lain. Anda kemudian dapat mengenali prosesnya pada langkah berikutnya.
449* **Environment-nya**: jadikan runner tersebut satu-satunya runner di sana, misalnya di environment yang Anda [buat untuk pengujian](https://code.claude.com/docs/id/self-hosted-environments-quickstart.md#set-up-manually). Sesi dirutekan ke environment dan bukan ke runner.
450
451Jangan mulai sesi dulu, karena Anda memerlukan direktori dari langkah berikutnya sebelum build dimulai.
452
453**2. Temukan direktori cgroup runner**
454
455Pada host, bukan di dalam kontainer, temukan ID proses runner, misalnya dengan `ps`. Baca `/proc/<pid>/cgroup` untuk ID tersebut:
456
457* **Sebuah baris berisi `:pids:`**: host menggunakan cgroup v1, dan direktorinya adalah `/sys/fs/cgroup/pids` diikuti teks setelah `:pids:`
458* **Satu-satunya baris dimulai dengan `0::`**: host menggunakan cgroup v2, dan direktorinya adalah `/sys/fs/cgroup` diikuti teks setelah `0::`
459
460**3. Jalankan build representatif dan catat puncaknya**
461
462Jalankan satu sesi melalui build representatif, dan ambil puncaknya dari direktori cgroup runner:
463
464* **Direktori memiliki `pids.peak`**: baca file tersebut setelah build selesai
465* **Direktori tidak memiliki `pids.peak`**: baca `pids.current` sekali setiap detik selama build berjalan, dan simpan nilai tertinggi. Puncak di antara dua pembacaan tidak akan terlihat, jadi perlakukan nilai tersebut sebagai perkiraan rendah.
466
467**4. Kalikan dengan sesi yang berbagi batas**
468
469* **Pod, kontainer, atau unit satu runner**: kalikan puncak dengan `--capacity` runner
470* **Batas yang dibagi oleh beberapa runner**: kalikan puncak dengan jumlah kapasitas mereka
471
472Kemudian tambahkan puncak dari hal lain yang berbagi batas tersebut, seperti kontainer lain di Pod.
473
474**5. Atur batas di atas hasilnya**
475
476Dengan batas yang lebih tinggi, Anda menyisakan ruang untuk build yang lebih berat, dan dengan batas yang lebih rendah, Anda menghentikan fork bomb lebih cepat.
477
478#### Periksa batas `pids.max`
479
480* **Periksa apakah `pids.max` telah tercapai**: cari di log kernel host, misalnya dengan `dmesg`, baris yang berisi `cgroup: fork rejected by pids controller in` dan path sebuah cgroup. Kernel hanya mencatatnya pertama kali ia menolak proses atau thread dalam cgroup tersebut, jadi cari di seluruh log.
481* **Lihat batas yang berlaku**: baca `pids.max` di direktori cgroup runner dan di setiap direktori di atasnya. Nilai terendah adalah batas yang berlaku, dan `max` berarti tidak ada batas pada tingkat tersebut.
383 482
384## Kubernetes483## Kubernetes
385 484
577 676
578Apa jalur penggunaan kembali lakukan dan tidak jamin:677Apa jalur penggunaan kembali lakukan dan tidak jamin:
579 678
580* **Bentuk klon apa pun bekerja**: klon penuh, shallow, atau single-branch di jalur digunakan apa adanya. Runner tidak pernah melewatkan `--depth` ketika mengambil ke klon yang ada, jadi pre-warm penuh menyimpan riwayat penuhnya dan yang shallow tetap shallow. `CLAUDE_RUNNER_FETCH_DEPTH` (`full`, `0`, atau angka; default 50) mengontrol hanya klon dingin yang dibuat runner ketika tidak ada klon yang ada.679* **Kedalaman klon**: lakukan build klon tanpa `--depth`, dengan atau tanpa `--single-branch`. Runner tidak pernah meneruskan `--depth` saat mengambil ke dalam klon yang memiliki remote `origin`, bahkan jika Anda menetapkan [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/id/self-hosted-environments-reference#environment-variable-only-settings). Dengan klon penuh, setiap sesi hanya mengunduh commit yang belum dimiliki klon. Dengan klon shallow, sesi pada branch yang bercabang sebelum commit tertua klon akan mengunduh seluruh riwayat branch tersebut.
581* **Perubahan terlacak di-reset, file tidak terlacak bertahan**: pada `--capacity 1`, setiap sesi dimulai dari reset keras yang menghapus modifikasi terlacak dari sesi sebelumnya, tetapi runner tidak pernah menjalankan `git clean`, sehingga file tidak terlacak dari sesi-sesi sebelumnya milik pemilik terkunci tetap ada di pohon.680* **Perubahan terlacak di-reset, file tidak terlacak bertahan**: pada `--capacity 1`, setiap sesi dimulai dari reset keras yang menghapus modifikasi terlacak dari sesi sebelumnya, tetapi runner tidak pernah menjalankan `git clean`, sehingga file tidak terlacak dari sesi-sesi sebelumnya milik pemilik terkunci tetap ada di pohon.
582* **Direktori per-sesi juga bertahan**: di samping checkout, runner membuat entri per-sesi di bawah `<base-dir>/_sessions/` untuk setiap sesi yang dijalankannya. Direktori konfigurasi Claude sesi menyimpan salinan lokal transkrip percakapan. Di sebelahnya duduk file yang diunggah sesi, ketika sesi memiliki apa pun. Direktori sesi duduk di sana juga: itu menyimpan worktree per-sesi apa pun dan checkout hook `checkout` sementara sesi berjalan, dan itu menyimpan apa pun yang Claude tulis di dalamnya.681* **Direktori per-sesi juga bertahan**: di samping checkout, runner membuat entri per-sesi di bawah `<base-dir>/_sessions/` untuk setiap sesi yang dijalankannya. Direktori konfigurasi Claude sesi menyimpan salinan lokal transkrip percakapan. Di sebelahnya duduk file yang diunggah sesi, ketika sesi memiliki apa pun. Direktori sesi duduk di sana juga: itu menyimpan worktree per-sesi apa pun dan checkout hook `checkout` sementara sesi berjalan, dan itu menyimpan apa pun yang Claude tulis di dalamnya.
583 682
587* **Dengan proxy git, pre-warm `reftable` kehilangan ref-nya**: saat runner membersihkan `.git/` sebelum setiap sesi, runner menyimpan `.git/refs/` dan menghapus direktori `.git/reftable/`. Klon yang dipanaskan sebelumnya dalam format ref `reftable` milik git kehilangan ref-nya dengan cara itu, jadi lakukan pre-warm klon dalam format ref `files` milik git sebagai gantinya.686* **Dengan proxy git, pre-warm `reftable` kehilangan ref-nya**: saat runner membersihkan `.git/` sebelum setiap sesi, runner menyimpan `.git/refs/` dan menghapus direktori `.git/reftable/`. Klon yang dipanaskan sebelumnya dalam format ref `reftable` milik git kehilangan ref-nya dengan cara itu, jadi lakukan pre-warm klon dalam format ref `files` milik git sebagai gantinya.
588* **Klon panjang tidak memerlukan workaround**: runner membatasi setiap operasi git dengan watchdog no-progress 120 detik dan hard cap 30 menit, bukan timeout flat, jadi klon dingin lambat yang terus melaporkan kemajuan selesai.687* **Klon panjang tidak memerlukan workaround**: runner membatasi setiap operasi git dengan watchdog no-progress 120 detik dan hard cap 30 menit, bukan timeout flat, jadi klon dingin lambat yang terus melaporkan kemajuan selesai.
589 688
689### Pastikan bahwa runner menggunakan klon Anda
690
691Untuk memastikan bahwa runner menggunakan klon Anda, mulai runner dengan [`--log-level debug`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags) dan mulai sesi pada repositori tersebut. Runner menulis `Prefetched repo found at` beserta jalur klon ke log. Jika runner justru menulis `No prefetched repo, fresh init at` ke log, runner tidak menemukan klon yang dapat digunakan di jalur pada baris tersebut. Periksa bahwa klon Anda berada di jalur tersebut, dimiliki oleh pengguna runner, dan memiliki remote bernama `origin`.
692
693Jika runner menemukan klon Anda tetapi sesi masih dimulai dengan lambat, periksa apakah klon tersebut shallow.
694
590## Sematkan versi695## Sematkan versi
591 696
592Secara default, proses anak setiap sesi menjalankan biner runner itu sendiri, dan runner mematikan auto-update di dalam sesi yang dihasilkannya. Dengan default tersebut, setiap sesi menjalankan versi yang Anda instal di host atau yang di-build ke dalam gambar. Update tingkat host berlaku waktu berikutnya runner dimulai.697Secara default, proses anak setiap sesi menjalankan biner runner itu sendiri, dan runner mematikan auto-update di dalam sesi yang dihasilkannya. Dengan default tersebut, setiap sesi menjalankan versi yang Anda instal di host atau yang di-build ke dalam gambar. Update tingkat host berlaku waktu berikutnya runner dimulai.
609* **Armada tetap**: jalankan set replika runner statis dan skala pada [metrik Prometheus](https://code.claude.com/docs/id/self-hosted-environments-reference.md#prometheus-metrics) yang dilayani setiap runner714* **Armada tetap**: jalankan set replika runner statis dan skala pada [metrik Prometheus](https://code.claude.com/docs/id/self-hosted-environments-reference.md#prometheus-metrics) yang dilayani setiap runner
610* **Runner on-demand**: jalankan subperintah `claude self-hosted-runner orchestrator`, yang menanyakan Anthropic untuk sesi yang antri tanpa runner tersedia dan memanggil hook `spawn-runner` Anda untuk boot satu per sesi. Lihat [On-demand runners](https://code.claude.com/docs/id/self-hosted-environments-configuration.md#on-demand-runners).715* **Runner on-demand**: jalankan subperintah `claude self-hosted-runner orchestrator`, yang menanyakan Anthropic untuk sesi yang antri tanpa runner tersedia dan memanggil hook `spawn-runner` Anda untuk boot satu per sesi. Lihat [On-demand runners](https://code.claude.com/docs/id/self-hosted-environments-configuration.md#on-demand-runners).
611 716
717### Lepaskan sesi idle pada armada tetap
718
719Jika sesi tetap mengantri pada armada tetap sementara setiap runner online, sesi idle mungkin penyebabnya. Secara default, sebuah sesi mempertahankan slotnya setelah gilirannya selesai, dan runner-nya tetap terkunci pada [pemilik](https://code.claude.com/docs/id/self-hosted-environments#key-concepts) sesi tersebut. Jika [tugas terjadwal cloud](https://code.claude.com/docs/id/routines.md) tim Anda memulai sesi baru pada setiap eksekusi, sesi idle-nya dapat memenuhi setiap slot.
720
721Untuk memeriksanya, baca `claude_code_self_hosted_runner_session_idle_seconds` di [metrik Prometheus](https://code.claude.com/docs/id/self-hosted-environments-reference.md#prometheus-metrics) setiap runner. Setiap sesi yang dipegang runner memiliki seri tersendiri: `0` selama sesi sibuk, jika tidak, jumlah detik sejak sesi tersebut menjadi idle. Pilih waktu idle yang Anda izinkan: lebih lama dari jeda yang diambil orang di tengah tugas, dan cukup singkat sehingga setiap eksekusi dilepaskan sebelum eksekusi berikutnya dari tugas terjadwal dimulai. Bandingkan seri tersebut dengan waktu itu.
722
723* **Seri di atas waktu tersebut, seperti `1800` untuk 30 menit**: sesi idle memegang slot tersebut, jadi atur `--release-idle-session-min`
724* **Setiap seri di bawahnya**: tidak ada sesi yang idle selama itu. Tambahkan runner, atau atur [`--kill-session-after-min`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags) untuk [sesi yang tidak pernah menjadi idle](#some-sessions-don’t-count-as-idle).
725* **Runner tanpa seri**: tidak ada sesi yang memegangnya. Lihat **Sessions stay queued** di bawah [Pemecahan masalah](#troubleshooting).
726* **Runner yang metriknya tidak merespons**: prosesnya mungkin telah keluar. Lihat **An environment that looks healthy** di bawah [When the runner exits](#when-the-runner-exits).
727
728Untuk melepaskan sesi idle, mulai setiap runner dengan [`--release-idle-session-min`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags) dan waktu tersebut dalam menit, seperti `--release-idle-session-min 30`. Runner kemudian melepaskan setiap sesi yang tetap idle selama itu, baik yang dimulai oleh tugas terjadwal maupun oleh seseorang.
729
730* **Pekerjaan yang belum di-push**: sesi yang dilepaskan dilanjutkan pada pesan berikutnya, tanpa pekerjaan yang belum di-push. Untuk mempertahankan commit-nya, lihat [Resumed sessions lose unpushed work](#additional-limitations).
731* **Runner keluar**: runner yang melepaskan sesi terakhirnya memulai [keluar normal](#when-the-runner-exits). Minta supervisor Anda memulai ulang runner tersebut pada filesystem baru, seperti yang dijelaskan di [Harden your deployment](#harden-your-deployment).
732* **Sesi yang tidak pernah menjadi idle**: atur juga [`--kill-session-after-min`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags), karena [beberapa sesi tidak dihitung sebagai idle](#some-sessions-don’t-count-as-idle)
733
612## Masalah dan batasan yang diketahui734## Masalah dan batasan yang diketahui
613 735
614Berikut adalah batasan dalam rilis ini, dengan workaround di mana ada.736Berikut adalah batasan dalam rilis ini, dengan workaround di mana ada.
642### Batasan tambahan764### Batasan tambahan
643 765
644* **Sesi yang dilanjutkan kehilangan pekerjaan yang belum di-push**: runner segar mengklon repositori lagi dari branch awalnya, jadi pekerjaan yang belum di-push oleh sesi hilang.766* **Sesi yang dilanjutkan kehilangan pekerjaan yang belum di-push**: runner segar mengklon repositori lagi dari branch awalnya, jadi pekerjaan yang belum di-push oleh sesi hilang.
645 * **Untuk mempertahankan pekerjaan yang sudah di-commit**: atur [`--push-outcome-on-release`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags) pada setiap runner di lingkungan tersebut, karena runner tanpa flag ini melanjutkan sesi dari branch awalnya. Runner dengan flag ini melakukan push best-effort atas branch hasil sesi sebelum melepaskannya, dan sesi yang dilanjutkan dimulai dari commit tersebut. Push tersebut menggunakan kredensial git milik host runner sendiri, termasuk pada runner yang menggunakan [git yang dikelola Anthropic](#use-the-anthropic-git-proxy). Perubahan yang belum di-commit tetap hilang.767 * **Untuk mempertahankan pekerjaan yang sudah di-commit**: atur [`--push-outcome-on-release`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags) pada setiap runner di lingkungan tersebut, karena runner tanpa flag ini melanjutkan sesi dari branch awalnya. Runner dengan flag ini melakukan push best-effort atas branch hasil sesi sebelum melepaskannya, dan sesi yang dilanjutkan dimulai dari commit tersebut. Push tersebut menggunakan kredensial git milik host runner sendiri, termasuk pada runner yang menggunakan [git yang dikelola Anthropic](#use-the-anthropic-git-proxy).
646 * **Dengan hook `checkout`**: repositori yang di-checkout melalui [hook siklus hidup `checkout`](https://code.claude.com/docs/id/self-hosted-environments-configuration#checkout) tidak di-push. Buat snapshot repositori tersebut dari [hook `post-session`](https://code.claude.com/docs/id/self-hosted-environments-configuration#post-session) sebagai gantinya.768 * **Dengan hook `checkout`**: repositori yang di-checkout melalui [hook siklus hidup `checkout`](https://code.claude.com/docs/id/self-hosted-environments-configuration#checkout) tidak di-push. Buat snapshot repositori tersebut dari [hook `post-session`](https://code.claude.com/docs/id/self-hosted-environments-configuration#post-session) sebagai gantinya.
647 * **Sebelum mengaktifkan flag ini**: batasi siapa yang dapat melakukan push ke ref `claude/*` pada remote sumber. Saat dilanjutkan, runner mengambil branch yang sebelumnya di-push tanpa memverifikasi siapa yang melakukan push-nya.769 * **Sebelum mengaktifkan flag ini**: batasi siapa yang dapat melakukan push ke ref `claude/*` pada remote sumber. Saat dilanjutkan, runner mengambil branch yang sebelumnya di-push tanpa memverifikasi siapa yang melakukan push-nya.
770 * **Perubahan yang belum di-commit tetap hilang**: untuk meminta Claude melakukan commit dan push ketika selesai merespons, lihat [Prompt sessions to push their work](https://code.claude.com/docs/id/self-hosted-environments-configuration.md#prompt-sessions-to-push-their-work)
648* **Repositori yang ditambahkan di tengah sesi dapat gagal diklon**: Claude mengklonnya dengan `git clone` melalui HTTPS. Pada runner tanpa [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), klon gagal dengan kesalahan autentikasi git jika tidak ada apa pun di host yang dapat membaca repositori tersebut. Jika memungkinkan, pilih setiap repositori yang dibutuhkan sesi ketika Anda membuatnya.771* **Repositori yang ditambahkan di tengah sesi dapat gagal diklon**: Claude mengklonnya dengan `git clone` melalui HTTPS. Pada runner tanpa [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), klon gagal dengan kesalahan autentikasi git jika tidak ada apa pun di host yang dapat membaca repositori tersebut. Jika memungkinkan, pilih setiap repositori yang dibutuhkan sesi ketika Anda membuatnya.
649* **Beberapa konektor tidak muncul dalam sesi yang di-host sendiri**: konektor yang belum Anda hubungkan dalam pengaturan claude.ai tidak terdaftar dalam sesi yang di-host sendiri, dan sesi tidak akan meminta Anda untuk menghubungkannya. Hubungkan dalam Pengaturan terlebih dahulu, kemudian mulai sesi segar. Menambahkan konektor ke sesi yang sudah berjalan juga tidak membuat alat-alatnya tersedia untuk Claude; mulai sesi segar untuk mengambil konektor yang baru ditambahkan.772* **Beberapa konektor tidak muncul dalam sesi yang di-host sendiri**: konektor yang belum Anda hubungkan dalam pengaturan claude.ai tidak terdaftar dalam sesi yang di-host sendiri, dan sesi tidak akan meminta Anda untuk menghubungkannya. Hubungkan dalam Pengaturan terlebih dahulu, kemudian mulai sesi segar. Menambahkan konektor ke sesi yang sudah berjalan juga tidak membuat alat-alatnya tersedia untuk Claude; mulai sesi segar untuk mengambil konektor yang baru ditambahkan.
650* **Ketika seseorang mengarsipkan atau menghapus sesi, atau melepaskan runner-nya dari menu sesi**: jangan mengandalkan push [`--push-outcome-on-release`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags). Untuk menyimpan commit dalam kasus tersebut, push `HEAD` ke branch penyelamat dari [hook `post-session`](https://code.claude.com/docs/id/self-hosted-environments-configuration#post-session). Jika Anda memulai dari contoh di sana, ubah agar contoh tersebut melakukan push bahkan ketika tidak ada perubahan yang belum di-commit, karena contoh tersebut hanya melakukan push setelah commit snapshot.773* **Ketika seseorang mengarsipkan atau menghapus sesi, atau melepaskan runner-nya dari menu sesi**: jangan mengandalkan push [`--push-outcome-on-release`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags). Untuk menyimpan commit dalam kasus tersebut, push `HEAD` ke branch penyelamat dari [hook `post-session`](https://code.claude.com/docs/id/self-hosted-environments-configuration#post-session). Jika Anda memulai dari contoh di sana, ubah agar contoh tersebut melakukan push bahkan ketika tidak ada perubahan yang belum di-commit, karena contoh tersebut hanya melakukan push setelah commit snapshot.
684* **Sesi gagal segera setelah diambil**: buka sesi di claude.ai/code untuk melihat kesalahannya. Jika kesalahan menunjukkan bahwa git tidak dapat melakukan autentikasi, periksa [kredensial git](#configure-git) runner. Pada runner yang dimulai dengan `--use-anthropic-git-proxy`, lihat [Ketika sesi gagal dimulai pada runner dengan git proxy](#when-anthropic-doesnt-serve-a-session).807* **Sesi gagal segera setelah diambil**: buka sesi di claude.ai/code untuk melihat kesalahannya. Jika kesalahan menunjukkan bahwa git tidak dapat melakukan autentikasi, periksa [kredensial git](#configure-git) runner. Pada runner yang dimulai dengan `--use-anthropic-git-proxy`, lihat [Ketika sesi gagal dimulai pada runner dengan git proxy](#when-anthropic-doesnt-serve-a-session).
685 808
686 Direktori dasar yang tidak dapat ditulisi menghentikan runner saat startup alih-alih menggagalkan sesi. Lihat entri **Runner keluar saat startup dengan `cannot create or write to base directory`** dalam daftar ini.809 Direktori dasar yang tidak dapat ditulisi menghentikan runner saat startup alih-alih menggagalkan sesi. Lihat entri **Runner keluar saat startup dengan `cannot create or write to base directory`** dalam daftar ini.
810* **Sesi diambil, tetapi Claude tidak pernah menjawab pesan pertamanya**: jika runner Anda menjangkau `api.anthropic.com` melalui proxy, konfigurasikan proxy tersebut agar meneruskan respons `/worker/events/stream` sesi saat respons tersebut tiba. Menaikkan timeout runner tidak membantu. Untuk memastikan penyebabnya di log runner, lihat [Sesi diambil, tetapi Claude tidak pernah menjawab pesan pertamanya](#when-a-sessions-first-message-is-never-answered).
687* **Sesi gagal dimulai pada runner yang mengatur `--use-anthropic-git-proxy`**: cari `access denied by the git proxy` di log runner, atau kesalahan git yang menyebutkan alamat `api.anthropic.com` yang berisi `/git_proxy/`. Untuk mengetahui apakah Anthropic melayani sesi tersebut dan memperbaiki penyebabnya, lihat [Ketika sesi gagal dimulai pada runner dengan git proxy](#when-anthropic-doesnt-serve-a-session).811* **Sesi gagal dimulai pada runner yang mengatur `--use-anthropic-git-proxy`**: cari `access denied by the git proxy` di log runner, atau kesalahan git yang menyebutkan alamat `api.anthropic.com` yang berisi `/git_proxy/`. Untuk mengetahui apakah Anthropic melayani sesi tersebut dan memperbaiki penyebabnya, lihat [Ketika sesi gagal dimulai pada runner dengan git proxy](#when-anthropic-doesnt-serve-a-session).
688* **Sesi tidak dapat menjangkau jaringan melalui proxy egress yang melakukan autentikasi**: ketika sumber yang Anda atur dengan [`--proxy-authorization-command` atau `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) gagal, mengalami timeout setelah 30 detik, atau menghasilkan nilai kosong, runner menjawab koneksi tersebut dengan `502 Bad Gateway` dan mencatat alasannya ke log. Runner menyamarkan stderr perintah tersebut di log itu dan tidak pernah mencatat nilai header ke log. Dengan `--proxy-authorization-command`, jalankan sendiri perintah tersebut di host untuk memastikan perintah itu mencetak seluruh nilai header ke stdout. Jika runner justru keluar saat startup dengan `could not start the proxy-authorization listener`, runner tidak dapat membuka listener loopback-nya.812* **Sesi tidak dapat menjangkau jaringan melalui proxy egress yang melakukan autentikasi**: ketika sumber yang Anda atur dengan [`--proxy-authorization-command` atau `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) gagal, mengalami timeout setelah 30 detik, atau menghasilkan nilai kosong, runner menjawab koneksi tersebut dengan `502 Bad Gateway` dan mencatat alasannya ke log. Runner menyamarkan stderr perintah tersebut di log itu dan tidak pernah mencatat nilai header ke log. Dengan `--proxy-authorization-command`, jalankan sendiri perintah tersebut di host untuk memastikan perintah itu mencetak seluruh nilai header ke stdout. Jika runner justru keluar saat startup dengan `could not start the proxy-authorization listener`, runner tidak dapat membuka listener loopback-nya.
689* **Runner mencatat baris `Poll failed` yang berisi `rejecting the malformed poll response`**: runner menerima respons work-poll yang body-nya bukan JSON yang diharapkan oleh antrean. Salah satu penyebabnya adalah proxy yang mencegat atau captive portal yang menjawab dengan halamannya sendiri. Jika runner terhubung melalui proxy, konfigurasikan proxy agar meneruskan respons dari `api.anthropic.com` tanpa diubah.813* **Runner mencatat baris `Poll failed` yang berisi `rejecting the malformed poll response`**: runner menerima respons work-poll yang body-nya bukan JSON yang diharapkan oleh antrean. Salah satu penyebabnya adalah proxy yang mencegat atau captive portal yang menjawab dengan halamannya sendiri. Jika runner terhubung melalui proxy, konfigurasikan proxy agar meneruskan respons dari `api.anthropic.com` tanpa diubah.
708* **Sesi gagal dimulai dengan `This runner couldn't read`**: runner meminta repositori dari host git dengan [kredensial git](#configure-git)-nya dan mendapat jawaban tidak ditemukan. Repositori tersebut tidak ada, atau kredensial tersebut tidak dapat menjangkau repositori itu.832* **Sesi gagal dimulai dengan `This runner couldn't read`**: runner meminta repositori dari host git dengan [kredensial git](#configure-git)-nya dan mendapat jawaban tidak ditemukan. Repositori tersebut tidak ada, atau kredensial tersebut tidak dapat menjangkau repositori itu.
709 * **Jika Anda mengelola runner**: berikan akses baca ke repositori tersebut kepada kredensial git runner, lalu mulai sesi baru.833 * **Jika Anda mengelola runner**: berikan akses baca ke repositori tersebut kepada kredensial git runner, lalu mulai sesi baru.
710 * **Jika Anda yang memulai sesi**: jika repositori tidak ada, mulai sesi baru dengan repositori yang benar. Jika runner tidak dapat menjangkau repositori tersebut, minta administrator runner untuk memberikan akses, lalu mulai sesi baru.834 * **Jika Anda yang memulai sesi**: jika repositori tidak ada, mulai sesi baru dengan repositori yang benar. Jika runner tidak dapat menjangkau repositori tersebut, minta administrator runner untuk memberikan akses, lalu mulai sesi baru.
711* **Sesi membutuhkan waktu beberapa menit untuk dimulai**: clone awal biasanya mendominasi. Pantau [metrik](https://code.claude.com/docs/id/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_session_init_duration_seconds` untuk memastikannya, dan kurangi waktu clone dengan [checkout yang sudah dipanaskan sebelumnya](#reuse-a-pre-warmed-checkout) atau `CLAUDE_RUNNER_FETCH_DEPTH` yang lebih kecil.835* **Sesi membutuhkan waktu beberapa menit untuk dimulai atau dilanjutkan**: salah satu penyebabnya adalah clone baru. Pada fleet yang memulai setiap runner pada disk kosong, tanpa [hook `checkout`](https://code.claude.com/docs/id/self-hosted-environments-configuration#checkout), runner meng-clone ulang repositori sesi dari awal sebelum memulai Claude Code. Sesi yang dilanjutkan pada disk kosong menunggu clone yang sama lagi.
836
837 Untuk melewati clone baru, sediakan [checkout yang sudah dipanaskan sebelumnya](#reuse-a-pre-warmed-checkout). Jika Anda sudah menyediakannya, [pastikan runner menggunakannya](#confirm-that-the-runner-uses-your-clone). Ketika runner membuat clone sendiri dan keluar setelah satu sesi, turunkan [`CLAUDE_RUNNER_FETCH_DEPTH`](https://code.claude.com/docs/id/self-hosted-environments-reference#environment-variable-only-settings) dari default-nya sebanyak 50 commit untuk mengambil lebih sedikit riwayat.
838
839 Jika Anda menggunakan hook, hook tersebut berjalan sebagai pengganti clone runner, jadi periksa apa yang dilakukan hook Anda. Pantau [metrik](https://code.claude.com/docs/id/self-hosted-environments-reference.md#prometheus-metrics) `claude_code_self_hosted_runner_session_init_duration_seconds` untuk melihat berapa lama proses mulai berlangsung.
712* **Giliran gagal dengan 401**: ketika sebuah giliran berakhir dengan 401 atau 403 dari Anthropic API, runner mengambil [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/id/self-hosted-environments-configuration#wrapper-scripts) baru dari Anthropic dan meneruskannya ke sesi. Giliran yang gagal tidak dicoba ulang. Token ini berumur pendek, dan runner merotasinya melalui stdin sesi.840* **Giliran gagal dengan 401**: ketika sebuah giliran berakhir dengan 401 atau 403 dari Anthropic API, runner mengambil [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/id/self-hosted-environments-configuration#wrapper-scripts) baru dari Anthropic dan meneruskannya ke sesi. Giliran yang gagal tidak dicoba ulang. Token ini berumur pendek, dan runner merotasinya melalui stdin sesi.
713 841
714 Ketika pengambilan gagal, runner mencatat baris `inference_token refresh failed` yang menyebutkan kapan runner akan mencoba ulang, dan runner terus mencoba ulang selama sesi berjalan.842 Ketika pengambilan gagal, runner mencatat baris `inference_token refresh failed` yang menyebutkan kapan runner akan mencoba ulang, dan runner terus mencoba ulang selama sesi berjalan.
788 916
789Untuk diagnosis terpandu pada host runner, jalankan [subperintah doctor](#troubleshooting).917Untuk diagnosis terpandu pada host runner, jalankan [subperintah doctor](#troubleshooting).
790 918
919### Sesi diambil, tetapi Claude tidak pernah menjawab pesan pertamanya
920
921Jika runner mengambil sesi tetapi Claude tidak pernah menjawab pesan pertama, dan runner Anda menjangkau `api.anthropic.com` melalui proxy, proxy tersebut mungkin penyebabnya. Anthropic mengirimkan pesan-pesan ke sesi melalui satu respons streaming yang tetap terbuka. Jika proxy Anda melakukan buffering terhadapnya, atau mengakhirinya sebelum meneruskan apa pun, pesan pertama tidak pernah tiba. Permintaan lain dari sesi tetap berhasil.
922
923Untuk memperbaikinya, ubah cara proxy menangani permintaan ini:
924
925```text theme={null}
926GET https://api.anthropic.com/v1/code/sessions/<session-id>/worker/events/stream
927```
928
929* **Jangan lakukan buffering terhadap respons**: teruskan respons saat tiba. Content type-nya adalah `text/event-stream`.
930* **Biarkan respons tetap terbuka**: respons dapat tetap terbuka hingga satu jam. Jika proxy memiliki idle timeout, atur jauh di atas 15 detik, karena Anthropic secara default mengirimkan keep-alive setidaknya sesering itu.
931
932Kemudian mulai sesi baru dan periksa apakah Claude menjawab. Menaikkan timeout runner tidak membantu: Anda hanya menunggu lebih lama sebelum runner menghentikan sesi.
933
934#### Memastikan penyebabnya di log runner
935
936Langkah-langkah ini ditujukan untuk runner pada Claude Code v2.1.287 atau yang lebih baru, dengan `--log-level info` default. Runner yang lebih lama mencatat baris-baris ini lebih lambat, atau tidak sama sekali.
937
9381. Tunggu hingga [`--startup-timeout-min`](https://code.claude.com/docs/id/self-hosted-environments-reference#runner-cli-flags) berlalu, 15 menit secara default. Runner kemudian menghentikan sesi dan mencatat baris yang berisi `no child output for` dan diakhiri dengan `— releasing`.
9392. Cari `SDKStartup: SSE` di log runner. Baris-baris ini mengikuti baris `finished: interrupted`, beberapa baris setelah baris pada langkah 1. Sebagian besar tidak memiliki timestamp, sehingga pengumpul log dapat menampilkannya di tempat lain.
9403. Temukan baris tersebut dalam tabel ini:
941
942| Baris log berisi | Yang harus dilakukan |
943| :- | :- |
944| `SSE connect failed (no response in 30s)` atau `SSE liveness timeout` | Periksa apakah proxy meneruskan permintaan, meneruskan header dan data respons saat tiba, dan tidak memutus koneksi |
945| `SSE connect failed (HTTP` dan sebuah kode status | Periksa log proxy itu sendiri. Jika proxy menjawab permintaan tersebut sendiri, izinkan permintaan melewatinya. |
946| `SSE connect failed (` dan teks lainnya | Teks di dalam tanda kurung adalah kesalahannya. Cari koneksi yang sama di log proxy itu sendiri. |
947| `SSE stream ended by server`, berulang kali | Periksa batas waktu proxy untuk respons yang berumur panjang. Satu baris seperti itu adalah hal yang normal. |
948| `SSE reconnected after` | Cari salah satu baris lain setelahnya. Baris ini tidak berarti ada sesuatu yang telah mencapai sesi. |
949
950Jika log tidak memiliki satu pun dari baris-baris ini, atau log proxy tidak menunjukkan masalah apa pun, [laporkan masalah](#report-an-issue).
951
952#### Menghentikan sesi agar tidak mencoba lagi
953
954Sesi tetap menahan pesan tersebut dalam status tertunda, dan runner yang dapat melayani pemilik sesi akan mengambil sesi tersebut lagi. Hingga Anda mengubah proxy, sesi menunggu dengan cara yang sama setiap kali. Untuk menghentikannya, arsipkan sesi tersebut di claude.ai/code.
955
791## Apa selanjutnya956## Apa selanjutnya
792 957
793* [Sesuaikan sesi](https://code.claude.com/docs/id/self-hosted-environments-configuration.md): script wrapper, lifecycle hook, runner on-demand, server MCP, dan izin958* [Sesuaikan sesi](https://code.claude.com/docs/id/self-hosted-environments-configuration.md): script wrapper, lifecycle hook, runner on-demand, server MCP, dan izin