# Autoscaling Runner GitHub Actions di Kubernetes dengan ARC

> Cara saya menskalakan runner GitHub Actions ephemeral di Kubernetes dengan ARC — penskalaan berbasis antrean, scale-to-zero, mode dind vs kubernetes, dan pengaturan yang benar-benar mengontrol biaya.

URL: https://www.matthewswong.com/id/blog/github-actions-runner-autoscaling-arc/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-07-14
- Updated: 2026-07-14
- Category: DevOps

> **Key takeaway:** Actions Runner Controller (ARC) menjalankan runner self-hosted GitHub Actions Anda sebagai pod Kubernetes ephemeral. Listener-nya memantau antrean job dan menskalakan pod runner naik saat ada job antre lalu turun ke nol saat idle, jadi Anda berhenti membayar VM yang selalu menyala dan setiap job dimulai pada runner sekali pakai yang bersih.

Saya menjalankan runner self-hosted GitHub Actions di VM biasa dalam waktu lama, dan dua mode kegagalannya selalu sama. Entah beberapa runner yang selalu menyala menganggur membakar biaya sepanjang malam, atau lonjakan push membuat job antre sepuluh menit karena saya membatasi jumlah mesin. Lebih buruk lagi, runner persisten menumpuk state antar job, sehingga sisa node_modules, cache Docker yang teracuni, atau variabel lingkungan liar dari build sebelumnya bocor ke build berikutnya. Actions Runner Controller memperbaiki kedua masalah sekaligus, dan ini adalah cara resmi yang direkomendasikan GitHub untuk menskalakan runner di Kubernetes.

Tulisan ini membahas arsitektur ARC modern (runner scale sets, bukan CRD RunnerDeployment yang sudah usang), bagaimana penskalaan sebenarnya memutuskan berapa banyak pod dibuat, dua container mode yang harus Anda pilih, dan pengaturan spesifik yang mengontrol biaya. Semua di bawah ini berdasarkan Helm chart gha-runner-scale-set terkini yang diterbitkan GitHub.

## Cara ARC benar-benar menskalakan: listener dan runner ephemeral

ARC memiliki dua Helm chart. Chart controller memasang operator dan custom resource sekali per cluster. Chart runner scale set dipasang sekali per kumpulan runner, dan setiap instalasi membuat satu AutoscalingRunnerSet plus satu pod listener ringan. Listener itu menjaga koneksi long-poll ke layanan GitHub Actions. Ketika job yang menargetkan label runner Anda antre, GitHub memberi tahu listener berapa runner yang dibutuhkan, dan listener mem-patch EphemeralRunnerSet sehingga Kubernetes membuat sejumlah pod runner itu. Tidak ada HPA berbasis CPU dan tidak ada polling endpoint metrik di sini — penskalaan didorong langsung oleh antrean job nyata, itulah mengapa cepat sekaligus akurat.

Setiap runner yang dibuat ARC bersifat ephemeral. Ia mendaftar ke GitHub, mengambil tepat satu job, menjalankannya, lalu pod dihapus. Job berikutnya mendapat pod baru dari image yang sama. Jaminan sekali pakai itulah alasan utama memilih ARC dibanding runner persisten: tidak ada state yang terbawa, jadi build tidak bisa disabotase oleh apa pun yang berjalan sebelumnya. Biayanya adalah waktu cold-start — setiap job membayar untuk menarik image runner dan, dalam mode dind, menjalankan daemon Docker — yang Anda mitigasi dengan warm pool lewat minRunners.

## Memasang controller dan sebuah scale set

Instalasi berupa dua rilis Helm dari OCI registry GitHub. Peningkatan besar dibanding ARC lama adalah cert-manager tidak lagi menjadi prasyarat, sehingga instalasi controller benar-benar satu perintah. Pasang controller lebih dulu, lalu buat secret Kubernetes berisi kredensial GitHub Anda (PAT untuk mulai cepat, atau GitHub App untuk produksi), kemudian pasang scale set yang menunjuk ke URL repository, org, atau enterprise Anda.

Nama rilis Helm menjadi label runner. Apa pun nama instalasi scale set itulah string persis yang Anda taruh di runs-on. Jadi rilis di atas dikonsumsi oleh workflow seperti ini:

> PAT cukup untuk pengujian tetapi cepat menabrak batas rate API GitHub pada skala besar dan bisa kedaluwarsa. Untuk yang sebenarnya, daftarkan GitHub App dan mount kredensialnya lewat secret — ini mendapat batas rate jauh lebih tinggi dan tidak mati diam-diam saat token dirotasi. Migrasi belakangan berarti membuat ulang secret dan rolling restart, jadi lakukan sejak awal.

## Tombol yang mengontrol biaya

Dua nilai menentukan tagihan Anda. minRunners adalah jumlah runner idle yang dijaga tetap hangat dan siap; maxRunners adalah batas keras berapa pod yang boleh ada sekaligus. Setel minRunners ke 0 dan Anda menskalakan penuh ke nol — Anda tidak membayar apa pun saat tidak ada job, dengan konsekuensi cold start pada job pertama setelah periode sepi. Setel ke 1 atau 2 dan Anda menukar sedikit biaya idle demi pengambilan hampir instan selama jam kerja. Jumlah yang benar-benar berjalan pada suatu saat kira-kira minRunners ditambah jumlah job aktif, dibatasi oleh maxRunners.

- minRunners: 0 — scale-to-zero sejati. Terbaik untuk build malam hari, repo lalu lintas rendah, dan apa pun yang cold start 30–60 detik masih dapat diterima.
- minRunners: 1–2 — warm pool kecil agar push pertama pagi hari tidak menunggu. Biaya idle-nya satu atau dua pod, bukan armada VM penuh.
- maxRunners — setel untuk melindungi cluster, bukan antrean. Build matriks yang lepas kendali bisa mencoba membuat ratusan pod dan menggusur beban kerja Anda yang lain.
- resources.requests — tepatkan ukuran CPU dan memori per pod runner agar scheduler Kubernetes mengemasnya efisien alih-alih memesan node berlebihan.

> Pasangkan scale-to-zero dengan Cluster Autoscaler Kubernetes atau Karpenter. ARC menghapus pod runner saat idle, tetapi node tetap memakan biaya sampai ada yang menghapusnya juga. Dengan node autoscaling, pool CI yang sepi total bisa benar-benar turun ke nol node semalaman dan menyalakan node baru pada job antre pertama.

## Container mode: dind versus kubernetes

Jika workflow Anda hanya menjalankan skrip, tes, dan build secara langsung, Anda tidak butuh apa pun khusus. Namun begitu sebuah job menggunakan container action, service container, atau docker build, runner butuh cara menjalankan container. ARC memberi dua opsi containerMode, dan pilihannya punya implikasi keamanan dan kompleksitas nyata.

| Aspek | containerMode: dind | containerMode: kubernetes |
| --- | --- | --- |
| Cara kerja | Sidecar dockerd privileged berbagi socket dengan runner | Setiap langkah job berjalan sebagai pod terpisah lewat container hooks |
| Privilese | Memerlukan container privileged | Tidak perlu container privileged |
| Upaya setup | Rendah — langsung jalan | Lebih tinggi — butuh persistent volume / storage class |
| Isolasi | Lebih lemah; daemon Docker dibagi dalam pod | Lebih kuat; langkah terisolasi di tingkat pod |
| Terbaik untuk | Tim yang sekadar butuh docker build berjalan | Cluster sensitif keamanan yang menghindari pod privileged |

Rekomendasi default saya adalah dind untuk kebanyakan tim — paling tidak mengejutkan dan meniru apa yang dilakukan runner biasa. Gunakan mode kubernetes hanya ketika kebijakan cluster Anda melarang pod privileged, dan sediakan waktu untuk konfigurasi storage yang dibutuhkannya, karena sering menjegal orang. Satu jebakan yang patut diketahui: versi chart terbaru pernah memiliki bug di mana init container dind yang disuntikkan berperilaku tak terduga saat containerMode diset ke kubernetes, jadi pin versi chart Anda dan uji mode yang benar-benar Anda pilih sebelum menggulirkannya ke setiap repo.

## Apa yang benar-benar akan saya terapkan

Untuk tim kecil hingga menengah, saya akan memasang controller sekali, menjalankan satu scale set per lingkungan (satu pool CI umum dan mungkin pool terpisah untuk deploy), menyetel minRunners ke 1 selama jam kerja dan membiarkannya turun ke nol di luar itu, menyetel maxRunners ke angka yang aman di bawah kapasitas cluster, dan memakai mode dind dengan GitHub App. Konfigurasi itu memberi runner sekali pakai yang bersih, pengambilan di bawah satu menit sepanjang hari, dan tagihan mendekati nol di malam hari. Ini jauh lebih murah daripada armada VM yang selalu menyala dan jauh lebih aman daripada runner persisten, dan karena penskalaan digerakkan antrean, ia tidak pernah tertinggal di belakang lonjakan commit seperti autoscaling berbasis CPU.

## Sources

- Actions Runner Controller — GitHub Docs
- Deploying runner scale sets with Actions Runner Controller — GitHub Docs
- actions/actions-runner-controller — GitHub repository

## Source links

- https://docs.github.com/en/actions/concepts/runners/actions-runner-controller
- https://docs.github.com/en/actions/how-tos/manage-runners/use-actions-runner-controller/deploy-runner-scale-sets
- https://github.com/actions/actions-runner-controller

## FAQ

### Apa itu Actions Runner Controller (ARC)?

ARC adalah operator Kubernetes yang didukung resmi oleh GitHub untuk menjalankan dan menskalakan runner self-hosted GitHub Actions sebagai pod. Anda memasang controller sekali per cluster, lalu membuat satu atau lebih runner scale set. Sebuah listener memantau antrean job GitHub dan membuat pod runner ephemeral sesuai permintaan, lalu turun kembali ke nol saat idle.

### Bagaimana ARC memutuskan berapa runner yang dibuat?

Penskalaan digerakkan langsung oleh antrean job GitHub Actions, bukan oleh CPU atau endpoint metrik. Pod listener ringan menjaga koneksi long-poll ke GitHub; ketika job yang menargetkan label runner Anda antre, GitHub memberi tahu listener berapa runner yang dibutuhkan dan ia mem-patch EphemeralRunnerSet sehingga Kubernetes membuat sejumlah pod itu. Ini membuat penskalaan cepat sekaligus akurat.

### Apa beda minRunners dan maxRunners?

minRunners adalah jumlah runner idle yang dijaga tetap hangat dan siap, sedangkan maxRunners adalah batas keras total pod sekaligus. Menyetel minRunners ke 0 memberi scale-to-zero sejati sehingga Anda tidak membayar apa pun saat idle, dengan konsekuensi cold start pada job pertama. Jumlah yang berjalan kira-kira minRunners ditambah job aktif, dibatasi maxRunners.

### Sebaiknya pakai containerMode dind atau kubernetes?

Gunakan dind untuk kebanyakan tim — ia menjalankan sidecar dockerd privileged dan langsung berfungsi untuk docker build, service container, dan container action. Gunakan mode kubernetes ketika kebijakan keamanan Anda melarang pod privileged; ia menjalankan setiap langkah sebagai pod terpisah tetapi butuh persistent volume dan setup lebih banyak. Jika job Anda hanya menjalankan skrip dan tes, Anda tidak butuh keduanya.

### Apakah ARC masih memerlukan cert-manager?

Tidak. Versi lama ARC memerlukan cert-manager sebagai prasyarat, tetapi controller gha-runner-scale-set modern tidak lagi membutuhkannya, sehingga instalasi controller cukup satu perintah Helm. Pasangkan ARC dengan Cluster Autoscaler Kubernetes atau Karpenter agar node idle juga dihapus, bukan hanya pod runner idle.

---

- HTML version: https://www.matthewswong.com/id/blog/github-actions-runner-autoscaling-arc/
- English: https://www.matthewswong.com/en/blog/github-actions-runner-autoscaling-arc/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
