SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor

Cara Setup GitHub Actions Runner pada VPS Ubuntu 24.04

Panduan langkah demi langkah memasang GitHub Actions runner di VPS Ubuntu 24.04. Ketahui cara konfigurasi systemd, semakan checksum, dan risiko keselamatan pull request.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

Fungsi GitHub Actions runner yang dihoskan sendiri

GitHub Actions runner yang dihoskan sendiri ialah program yang anda pasang pada VPS anda sendiri untuk meminta tugasan daripada GitHub dan melaksanakannya pada perkakasan anda. Anda mendaftarkannya pada satu repositori, memasangnya sebagai servis systemd, dan ia akan berjalan semula selepas setiap but semula. GitHub menjadualkan tugasan tersebut. Pelayan anda melakukan kerja itu.

CI (integrasi berterusan) pada mesin milik anda berbaloi atas dua sebab. Minit binaan tidak lagi dikira, dan sesuatu tugasan boleh mencapai perkara yang hanya ada pada mesin anda, seperti cache binaan yang sedia ada atau rangkaian peribadi. Harganya ialah keselamatan. Runner melaksanakan apa sahaja yang dinyatakan dalam fail aliran kerja, sebagai pengguna yang anda tetapkan, jadi fail aliran kerja sememangnya merupakan pelaksanaan kod jauh (remote code execution). Pada repositori peribadi, ini tidak menjadi masalah kerana hanya orang yang anda percayai boleh menambah fail tersebut. Pada repositori awam, ini merupakan risiko sebenar, dan bahagian mengenai permintaan tarik (pull requests) daripada fork menjelaskan mekanismenya.

Segala yang dinyatakan di bawah adalah berdasarkan Ubuntu 24.04 dengan runner versi 2.338.0, iaitu keluaran semasa setakat 12 Oktober 2026.

Keperluan sebelum bermula

Mulakan dengan VPS yang mempunyai akaun pentadbir biasa dan sudo, iaitu keadaan yang anda capai dalam sepuluh minit pertama pada VPS baharu. Anda tidak perlu membuka port masuk. Runner membuka sambungan HTTPS (hypertext transfer protocol secure) keluar ke GitHub dan membiarkannya terbuka sementara menunggu tugasan, jadi GitHub tidak pernah menyambung ke pelayan anda. Firewall anda boleh kekal tertutup kepada dunia luar dan tugasan masih akan sampai.

Anda juga memerlukan hak pentadbir pada repositori tersebut, kerana token pendaftaran dipaparkan dalam tetapan repositori.

Cipta pengguna khusus untuk runner

Jangan sekali-kali menjalankan runner sebagai root atau sebagai pengguna pentadbir anda sendiri. Setiap tugasan mewarisi hak pengguna runner, jadi aliran kerja yang memanggil sudo akan berjaya jika pengguna runner boleh menggunakan sudo. Cipta satu pengguna tanpa keistimewaan yang tidak memiliki apa-apa kecuali direktori rumahnya sendiri. Akaun pengguna dengan keistimewaan paling rendah pada VPS merangkumi corak umum ini. Berikut adalah corak khusus untuknya.

sudo useradd -m -s /bin/bash gharunner
sudo passwd -l gharunner
sudo chmod 750 /home/gharunner
sudo install -d -m 700 -o gharunner -g gharunner /home/gharunner/actions-runner

passwd -l mengunci kata laluan, supaya tiada sesiapa boleh log masuk sebagai gharunner menggunakan kata laluan. Mod 700 pada direktori runner adalah penting kerana runner menyimpan kelayakannya di sana dalam bentuk teks jelas, dan daftar keluar (checkout) boleh mengandungi sumber peribadi.

Semak kedua-dua sifat ini sebelum anda meneruskan:

sudo passwd -S gharunner
sudo -l -U gharunner

passwd -S mencetak baris yang bermula dengan gharunner L, di mana L bermaksud kata laluan dikunci. sudo -l -U gharunner sepatutnya menjawab dengan is not allowed to run sudo. Jika ia mencetak senarai arahan yang dibenarkan sebaliknya, akaun tersebut berada dalam kumpulan sudo dan pengasingan yang baru anda bina telah hilang.

Muat turun runner dan semak tarball

Bekerja sebagai pengguna runner mulai sekarang.

sudo -iu gharunner
cd ~/actions-runner
RUNNER_VERSION=2.338.0
curl -fL -o actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
  "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"

Jalankan uname -m terlebih dahulu jika anda tidak pasti tentang seni bina sistem. x86_64 mengambil fail linux-x64 di atas. aarch64 mengambil actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz.

Sekarang, sahkan apa yang telah anda muat turun. SHA256 (secure hash algorithm, 256 bit) di bawah adalah untuk tarball x64 versi 2.338.0. GitHub memaparkan nilai untuk keluaran semasa pada halaman keluaran dan pada skrin New self-hosted runner. Nilai ini berubah bagi setiap versi, jadi salin nilai tersebut dari sana apabila anda memasang versi yang berbeza.

echo "af4b794c1bc41d73d40535e3fe092a39f9679cd8d965954c2aca25a05ca41d32  actions-runner-linux-x64-2.338.0.tar.gz" | sha256sum -c

Muat turun yang berjaya akan memaparkan satu baris:

actions-runner-linux-x64-2.338.0.tar.gz: OK

Fail yang terpotong atau diubah akan memaparkan kegagalan dan amaran:

actions-runner-linux-x64-2.338.0.tar.gz: FAILED
sha256sum: WARNING: 1 computed checksum did NOT match

Jangan langkau pemeriksaan ini dan membiarkan tar mencari masalah tersebut kemudian. Arkib yang tidak lengkap akan gagal dengan gzip: stdin: unexpected end of file dan tar: Unexpected EOF in archive, yang memberitahu anda bahawa fail tersebut rosak tetapi tidak menyatakan sama ada ia terpotong atau telah diganti.

tar xzf ./actions-runner-linux-x64-2.338.0.tar.gz
ls

Kandungan tarball dan perkara yang tidak disertakan

Selepas pengekstrakan, direktori tersebut mengandungi config.sh, run.sh, env.sh, safe_sleep.sh, bin/ dan externals/. bin/ menyimpan binari pelari (runner) dan bin/installdependencies.sh. externals/ menyimpan masa jalan (runtime) Node yang dibundel untuk pelaksanaan tindakan JavaScript.

svc.sh masih belum wujud. Dokumentasi GitHub menggambarkannya sebagai skrip "yang dicipta selepas berjaya menambah pelari", kerana ia ditulis daripada templat dengan nama repositori dan nama pelari anda yang disematkan ke dalam nama perkhidmatan. Oleh itu, sudo ./svc.sh install sebelum ./config.sh akan gagal dengan sudo: ./svc.sh: command not found. Lakukan pendaftaran terlebih dahulu, kemudian pasang perkhidmatan tersebut.

Memasang dependency runner

Runner ini merupakan aplikasi .NET, jadi ia memerlukan beberapa pustaka kongsi (shared libraries). Keluar daripada shell pengguna runner dan pasang pustaka tersebut menggunakan sudo, kerana skrip ini menulis ke dalam pangkalan data pakej sistem.

exit
cd /home/gharunner/actions-runner
sudo ./bin/installdependencies.sh

Pada Ubuntu 24.04, tindakan ini akan menarik libkrb5-3, zlib1g, liblttng-ust1t64, libssl3t64 dan libicu74. Skrip ini mencuba beberapa nama versi untuk setiap pustaka dan mengekalkan versi yang disediakan oleh release anda, itulah sebabnya skrip yang sama berfungsi pada Ubuntu versi lama dan Debian.

Jika anda melangkau langkah ini dan salah satu pustaka tersebut tiada, ./config.sh akan berhenti sebelum ia melakukan sebarang tindakan:

Dependencies is missing for Dotnet Core 6.0
Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies.

Ketiadaan libicu akan memberikan nasihat yang sama di bawah baris pertama yang berbeza, iaitu Libicu's dependencies is missing for Dotnet Core 6.0. Kedua-duanya datang daripada sumber yang sama: config.sh menyemak pustaka yang dibundel dengan ldd, dan libicu dengan ldconfig, sebelum ia bermula. Oleh itu, pautan yang tidak dapat diselesaikan akan menghentikan skrip tersebut daripada menyebabkan kegagalan yang mengelirukan kemudian hari.

Daftarkan runner dengan repositori anda

Dapatkan token daripada repositori. Buka Settings, kemudian Actions, kemudian Runners, dan seterusnya New self-hosted runner. Halaman tersebut akan memaparkan token pendaftaran. Token ini tamat tempoh satu jam selepas dijana, jadi jakan ia apabila anda sudah bersedia untuk menampalnya.

Daftar sebagai pengguna runner. config.sh enggan berjalan di bawah sudo.

sudo -iu gharunner
cd ~/actions-runner
./config.sh --url https://github.com/YOUR-USER/YOUR-REPO \
  --token PASTE_REGISTRATION_TOKEN_HERE \
  --name vps-runner-1 \
  --labels vps \
  --work _work \
  --unattended \
  --replace

Fungsi flag tersebut adalah seperti berikut. --name ialah cara runner dipaparkan dalam repositori, jadi pilih nama yang anda masih boleh kenali dalam tempoh enam bulan. --labels menambah label anda sendiri; runner sudah pun membawa self-hosted, Linux dan X64 secara automatik. --work menamakan direktori tempat checkout disimpan, di dalam direktori runner. --unattended menjawab gesaan interaktif dengan nilai lalai, iaitu tetapan yang diperlukan apabila arahan diletakkan dalam skrip. --replace mengambil alih pendaftaran sedia ada dengan nama yang sama dan bukannya gagal, iaitu tetapan yang diperlukan apabila anda membina semula pelayan.

Proses yang berjaya akan berakhir dengan baris berikut:

√ Runner successfully added
√ Runner connection is good
√ Settings Saved.

Pendaftaran kini disimpan dalam direktori runner sebagai .runner, .credentials dan .credentials_rsaparams. Dua fail terakhir mengenal pasti runner ini kepada GitHub, jadi sesiapa yang boleh membacanya boleh menyamar sebagai runner tersebut. Itulah sebabnya direktori tersebut ditetapkan kepada mod 700 dan pengguna tidak mempunyai akses sudo.

Memasang runner sebagai perkhidmatan systemd

./run.sh dalam terminal adalah memadai untuk satu ujian, tetapi ia akan mati apabila sesi SSH anda tamat. Pasang perkhidmatan tersebut supaya runner bermula semasa but. perkhidmatan dan pemasa systemd pada VPS menjelaskan fail unit itu sendiri. Di sini svc.sh menulis satu untuk anda.

exit
cd /home/gharunner/actions-runner
sudo ./svc.sh install gharunner
sudo ./svc.sh start
sudo ./svc.sh status

svc.sh memerlukan root kerana ia menulis unit ke dalam /etc/systemd/system dan mendayakannya. Argumen selepas install ialah pengguna yang menjalankan perkhidmatan tersebut. Berikan gharunner secara eksplisit. Tanpa argumen, skrip akan kembali kepada $SUDO_USER, iaitu akaun pentadbir anda, dan kemudian setiap tugasan akan dijalankan sebagai pengguna yang boleh menggunakan sudo.

Unit tersebut dinamakan mengikut repositori dan runner, dalam bentuk actions.runner.YOUR-USER-YOUR-REPO.vps-runner-1.service. Anda tidak perlu menaipnya secara manual:

systemctl list-units 'actions.runner.*'
sudo journalctl -u 'actions.runner.*' -n 20 --no-pager

Runner yang sihat akan mencatat √ Connected to GitHub dan kemudian satu baris yang berakhir dengan Listening for Jobs, dan halaman Runners pada repositori akan memaparkannya sebagai Idle. Runner yang dipaparkan sebagai Offline bermakna ia tidak berjalan atau tidak dapat mencapai GitHub pada port 443.

Menghantar tugasan kepada runner

runs-on memilih runner mengikut label. Minta self-hosted berserta label anda sendiri, supaya tugasan tidak dihantar kepada runner yang tidak anda inginkan.

name: build
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: [self-hosted, linux, vps]
    steps:
      - uses: actions/checkout@v5
      - run: uname -a

Jika tugasan menunggu di Waiting for a runner to pick up this job, label tersebut tidak sepadan. Setiap label dalam runs-on mesti wujud pada runner tersebut, jadi satu perkataan tambahan akan menyebabkan tugasan kekal dalam baris gilir tanpa sebarang ralat. Bandingkan senarai tersebut dengan label yang dipaparkan bersebelahan runner dalam tetapan repositori.

Mengapa runner yang dihoskan sendiri dan repositori awam tidak boleh dicampur

Ini adalah bahagian yang sering diabaikan oleh pengguna. Panduan GitHub menyatakan dengan jelas: runner yang dihoskan sendiri "hampir tidak boleh digunakan untuk repositori awam", dan ia "tidak mempunyai jaminan untuk berjalan dalam mesin maya sementara yang bersih, serta boleh dikompromi secara berterusan oleh kod yang tidak dipercayai dalam aliran kerja".

Mekanismenya mudah. Permintaan tarik (pull request) daripada fork membawa salinan fail aliran kerja sendiri. Jika repositori awam anda menjalankan aliran kerja permintaan tarik pada runner anda, sesiapa sahaja yang boleh melakukan fork pada repositori tersebut boleh mencadangkan aliran kerja yang menjalankan arahan mereka pada VPS anda. Mereka tidak memerlukan akses tulis, kerana perkara yang mereka cadangkan itulah yang akan dijalankan.

Tetapan kelulusan mengurangkan risiko ini tanpa menyelesaikannya sepenuhnya. Polisi lalai untuk repositori awam meminta penyelenggara meluluskan aliran kerja fork daripada penyumbang kali pertama. GitHub mengira seseorang sebagai penyumbang kali pertama hanya sehingga mana-mana komit atau permintaan tarik mereka digabungkan ke dalam repositori. Selepas itu, permintaan tarik mereka yang seterusnya akan berjalan tanpa sebarang gesaan, dan GitHub sendiri memberi amaran bahawa pembaikan kecil yang digabungkan sudah cukup untuk mencapai tahap tersebut. Jadi, kawalan keselamatan bergantung kepada manusia yang membaca diff, dan muatan (payload) yang disembunyikan tiga tahap di bawah dalam skrip binaan adalah mudah untuk terlepas pandang.

Permintaan tarik fork tidak menerima rahsia (secrets) anda, dan GITHUB_TOKEN miliknya adalah baca sahaja. Ini mengehadkan kerosakan di dalam GitHub. Ia tidak memberi kesan kepada pelayan anda. Penyerang mempunyai shell sebagai gharunner, jadi mereka boleh membaca setiap fail yang boleh dibaca oleh pengguna tersebut, mencapai apa sahaja yang boleh dicapai oleh VPS pada rangkaian peribadinya, dan meninggalkan sesuatu di dalam ~/.bashrc atau unit systemd pengguna yang berjalan semasa tugasan seterusnya.

Mendaftar dengan --ephemeral menyebabkan runner menerima satu tugasan dan kemudian menyahdaftar, jadi satu tugasan tidak boleh membaca ruang kerja tugasan yang seterusnya. Ia hanya membantu jika sesuatu membina semula mesin atau kontena untuk setiap tugasan, kerana pintu belakang (backdoor) yang ditulis ke dalam direktori utama pengguna runner akan kekal walaupun selepas pendaftaran baharu.

Peraturan yang berikut adalah ringkas. Gunakan runner yang dihoskan sendiri untuk repositori peribadi. Jika anda mesti menyambungkannya kepada repositori awam, jangan jalankan permintaan tarik fork padanya, jangan simpan apa-apa lagi pada pelayan tersebut, dan anggap mesin itu sebagai sesuatu yang boleh dibuang.

Tugasan Docker, dan kumpulan yang sebenarnya root

Tugasan kontena, kontena perkhidmatan, dan mana-mana langkah aliran kerja yang memanggil docker build memerlukan daemon Docker pada hos pelari. Pasang Docker dengan cara biasa, yang diliputi dalam Docker dan Docker Compose pada VPS, kemudian tambahkan pengguna pelari ke dalam kumpulan docker.

Fahami pertukaran tersebut sebelum anda melakukannya. Keahlian dalam kumpulan docker adalah setara dengan root, kerana kontena boleh melakukan bind mount pada / dan berjalan sebagai root di dalamnya. Jadi, aliran kerja yang boleh berhubung dengan soket Docker boleh membaca dan menulis setiap fail pada VPS, termasuk /etc/shadow. Pada repositori peribadi dengan penyumbang yang dipercayai, ini mungkin harga yang boleh diterima. Di tempat lain, ia menghilangkan tujuan pengguna tanpa keistimewaan. Rootless Docker mengekalkan binaan kontena dalam hak milik pengguna pelari itu sendiri, dengan kos pemacu storan yang lebih perlahan dan tiada kontena berkeistimewaan.

Kemas kini dan cara membuang runner dengan bersih

Runner yang dihoskan sendiri akan mengemas kini dirinya secara lalai. Ia mengesan keluaran baharu, menggantikan failnya sendiri dan memulakan semula servis, jadi biasanya anda tidak perlu melakukan apa-apa. Untuk mematikan kemas kini automatik, berikan --disableupdate kepada ./config.sh semasa anda mendaftarkan runner; pada runner yang telah didaftarkan, ini bermakna anda perlu mendaftarkannya semula dengan flag tersebut. Ia tidak membenarkan anda kekal pada satu versi selama-lamanya. Dokumentasi GitHub menyatakan dengan jelas bahawa runner yang dikonfigurasikan dengan --disableupdate masih perlu dikemas kini secara manual, dalam tempoh 30 hari selepas keluaran baharu, atau GitHub akan berhenti meletakkan tugasan dalam baris gilir untuknya.

Kemas kini manual mengekalkan pendaftaran, kerana .runner dan .credentials tidak disertakan dalam tarball. Hentikan servis, muat turun dan semak checksum tarball baharu sebagai gharunner, ekstrak ke dalam direktori yang sama dengan tar xzf, kemudian mulakan semula servis:

cd /home/gharunner/actions-runner
sudo ./svc.sh stop
sudo ./svc.sh start

Untuk membuang runner, nyahpasang servis terlebih dahulu, kemudian nyahdaftar. Token pembuangan diperoleh daripada halaman Runners yang sama, di bawah butang Remove milik runner tersebut.

cd /home/gharunner/actions-runner
sudo ./svc.sh stop
sudo ./svc.sh uninstall
sudo -iu gharunner
cd ~/actions-runner
./config.sh remove --token PASTE_REMOVAL_TOKEN_HERE

Memadam direktori tanpa menyahdaftar akan menyebabkan runner tersenarai sebagai Offline dalam repositori. Ia akan kekal di sana sehingga pentadbir mengklik Force remove this runner, atau sehingga GitHub membuangnya secara automatik selepas 14 hari tanpa sambungan.

Mod kegagalan, berserta rentetan yang akan anda lihat

Must not run with sudo. config.sh mencetak ini dan keluar apabila ia dijalankan sebagai root. Semakan ini dilakukan dengan sengaja, kerana fail milik root dalam _work akan merosakkan setiap tugasan seterusnya yang dijalankan sebagai pengguna servis. Jalankan ./config.sh sebagai gharunner. Pemboleh ubah RUNNER_ALLOW_RUNASROOT mengatasi semakan ini, dan menggunakannya hanya akan menangguhkan kerosakan tersebut ke peringkat kemudian.

sudo: ./svc.sh: command not found. Anda berada dalam direktori yang betul. svc.sh belum wujud lagi, kerana config.sh belum melengkapkan pendaftaran. Daftarkan runner, kemudian pasang servis tersebut.

Http response code: NotFound from 'POST https://api.github.com/actions/runner-registration'. Biasanya token tersebut bukanlah token pendaftaran yang sah. Sama ada ia telah tamat tempoh, memandangkan ia hanya bertahan selama satu jam, atau personal access token telah ditampal menggantikan token pendaftaran daripada halaman Runners. Jana token baharu dan tampal sekali lagi, serta pastikan --url adalah URL repositori yang lengkap.

Dependencies is missing for Dotnet Core 6.0. Jalankan sudo ./bin/installdependencies.sh daripada direktori runner sebagai root, kemudian daftar semula.

Runner Offline selepas but semula. Jalankan systemctl is-enabled 'actions.runner.*'. Jika tiada apa-apa yang disenaraikan, ./svc.sh install tidak pernah dijalankan, jadi runner tersebut hanya wujud di dalam sesi terminal anda sahaja. Jika unit telah diaktifkan dan runner masih Offline, baca journalctl -u 'actions.runner.*' dan periksa trafik HTTPS keluar.

Cakera penuh. Checkout, cache binaan dan imej Docker terkumpul di bawah _work dan dalam direktori utama pengguna runner, dan tiada proses yang akan membersihkannya untuk anda. Pantau du -sh /home/gharunner/actions-runner/_work dan tambahkan pembersihan berjadual sebelum cakera anda kehabisan ruang secara automatik.

FAQ

Mengapa sudo ./svc.sh install menyatakan command not found?

Kerana svc.sh tidak disertakan dalam tarball runner. Skrip ini dijana di dalam direktori runner apabila ./config.sh selesai mendaftar, dengan menggunakan nama repositori dan nama runner anda untuk membina nama servis. Jalankan ./config.sh sebagai pengguna runner terlebih dahulu. Selepas itu, sudo ./svc.sh install gharunner akan menemui skrip tersebut dan menulis unit bernama actions.runner.OWNER-REPO.RUNNER-NAME.service ke dalam /etc/systemd/system.

Adakah saya perlu membuka port firewall untuk runner yang dihoskan sendiri?

Tidak. Runner membuka sambungan HTTPS keluar ke GitHub dan mengekalkannya sementara menunggu tugasan, jadi GitHub tidak pernah memulakan sambungan ke VPS anda. Benarkan trafik keluar pada port 443 dan biarkan peraturan trafik masuk anda tertutup. Jika runner menunjukkan status Offline semasa servisnya sedang berjalan, periksa penapisan trafik keluar dan DNS dan bukannya peraturan trafik masuk.

Bolehkah saya menggunakan runner yang dihoskan sendiri pada repositori awam?

Anda boleh, namun GitHub tidak mengesyorkannya. Pull request daripada fork membawa fail workflow-nya sendiri, jadi sesiapa sahaja yang boleh melakukan fork pada repositori anda boleh mencadangkan arahan yang akan dijalankan pada mesin anda. Gesaan kelulusan lalai hanya meliputi penyumbang yang belum pernah mempunyai commit atau pull request yang digabungkan ke dalam repositori. Jika anda melampirkan runner pada repositori awam, lumpuhkan workflow pull request daripada fork, jangan simpan apa-apa selain itu pada pelayan tersebut, dan bina semula mesin mengikut jadual.

Mengapa pendaftaran gagal dengan Http response code: NotFound?

Panggilan pendaftaran menjawab NotFound apabila kelayakan salah, bukan hanya apabila URL salah, yang menjadikan mesej tersebut mengelirukan. Token pendaftaran tamat tempoh satu jam selepas ia dipaparkan, dan personal access token tidak diterima untuk panggilan ini. Buka Settings, Actions, Runners, New self-hosted runner sekali lagi, salin token baharu, dan pastikan nilai --url menghala ke repositori di mana anda mempunyai hak pentadbir.

#github-actions#ci#self-hosted#runner#ubuntu-24-04