SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-07

Self-hosted GitHub Actions runner sa Ubuntu 24.04

Irehistro ang self-hosted GitHub Actions runner sa Ubuntu 24.04 gamit ang dedicated user, checksum, config.sh at systemd, kasama ang panganib ng fork pull requests.

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

Ano ang ginagawa ng self-hosted GitHub Actions runner

Ang self-hosted GitHub Actions runner ay isang program na ini-install mo sa sarili mong VPS. Humihingi ito ng mga job mula sa GitHub at pinapatakbo ang mga ito sa hardware mo. Nirerehistro mo ito sa isang repository, ini-install bilang systemd service, at awtomatiko itong bumabalik pagkatapos ng bawat reboot. Si GitHub ang nagse-schedule ng job. Ang server mo ang nagsasagawa ng trabaho.

Mahalaga ang CI (continuous integration) sa sarili mong machine sa dalawang dahilan. Hindi na sinusukat ang build minutes, at maaaring ma-access ng isang job ang mga resource na mayroon lamang ang machine mo, gaya ng warm build cache o private network. Ang kapalit nito ay seguridad. Isinasagawa ng runner ang anumang nakasaad sa workflow file, gamit ang user na itinalaga mo rito. Dahil dito, ang workflow file ay remote code execution ayon sa disenyo. Sa private repository, katanggap-tanggap ito dahil ang mga taong pinagkakatiwalaan mo lamang ang maaaring magdagdag nito. Sa public repository, tunay itong panganib. Ipinapaliwanag ng seksiyon tungkol sa fork pull requests ang mekanismo nito.

Ang lahat ng sumusunod ay para sa Ubuntu 24.04 gamit ang runner version 2.336.0, ang kasalukuyang release noong July 2026.

Mga kailangan bago magsimula

Magsimula sa isang VPS na may karaniwang admin account at sudo, sa kalagayang naabot mo sa unang sampung minuto sa bagong VPS. Hindi mo kailangang magbukas ng inbound port. Nagbubukas ang runner ng outbound HTTPS (hypertext transfer protocol secure) connection papunta sa GitHub at pinananatili itong bukas habang naghihintay ng work. Kaya hindi kailanman kumokonekta ang GitHub sa iyong server. Maaaring manatiling sarado sa external traffic ang firewall mo at makararating pa rin ang mga job.

Kailangan mo rin ng admin rights sa repository dahil ipinapakita ang registration token sa repository settings.

Gumawa ng dedicated user para sa runner

Huwag kailanman patakbuhin ang runner bilang root o bilang sarili mong admin user. Namamana ng bawat job ang mga karapatan ng runner user, kaya magtatagumpay ang workflow na tumatawag sa sudo kung maaaring gumamit ng sudo ang runner user. Gumawa ng isang unprivileged user na sariling home directory lamang ang pagmamay-ari. Tinalakay sa Mga user account na may least privilege sa isang VPS ang pangkalahatang pattern. Narito ang partikular na setup.

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

Nilala-lock ng passwd -l ang password, kaya walang makakapag-log in bilang gharunner gamit ito. Mahalaga ang mode 700 sa runner directory dahil doon iniimbak ng runner ang mga credential nito bilang cleartext, at maaaring maglaman ang checkout ng private source.

Suriin ang dalawang property bago magpatuloy:

sudo passwd -S gharunner
sudo -l -U gharunner

Nagpi-print ang passwd -S ng linyang nagsisimula sa gharunner L, kung saan ang L ay nangangahulugang naka-lock ang password. Dapat magbalik ang sudo -l -U gharunner ng is not allowed to run sudo. Kung listahan ng mga pinapahintulutang command ang ipi-print nito, kabilang ang account sa isang sudo group at nawala na ang isolation na kakagawa mo lang.

I-download ang runner at i-check ang tarball

Mula rito, gamitin ang runner user.

sudo -iu gharunner
cd ~/actions-runner
RUNNER_VERSION=2.336.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"

Patakbuhin muna ang uname -m kung hindi ka sigurado sa architecture. Ginagamit ng x86_64 ang linux-x64 file sa itaas. Ginagamit ng aarch64 ang actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz.

Ngayon, i-verify kung ano ang na-download mo. Ang SHA256 (secure hash algorithm, 256 bit) sa ibaba ay para sa 2.336.0 x64 tarball. Ipinapakita ng GitHub ang value para sa kasalukuyang release sa release page at sa New self-hosted runner screen. Nagbabago ito sa bawat version, kaya kopyahin ang value mula roon kapag ibang version ang ini-install mo.

echo "04cf0be1aff4c3ec3554466c39124ca250e3effd8873bb7e8d68535aa9505d5d  actions-runner-linux-x64-2.336.0.tar.gz" | sha256sum -c

Ang matagumpay na download ay nagpi-print ng isang linya:

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

Ang truncated o nabagong file ay nagpi-print ng failure at warning:

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

Huwag laktawan ang check at hayaang tar ang makadiskubre ng problema. Ang half-written archive ay nagfa-fail sa gzip: stdin: unexpected end of file at tar: Unexpected EOF in archive. Ipinapakita nito na sira ang file, pero hindi kung naputol ang download o napalitan ang file.

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

Mga nilalaman ng tarball at mga wala rito

Pagkatapos i-extract, naglalaman ang directory ng config.sh, run.sh, env.sh, safe_sleep.sh, bin/ at externals/. Nasa bin/ ang runner binaries at bin/installdependencies.sh. Nasa externals/ ang naka-bundle na Node runtime na ginagamit para i-execute ang JavaScript actions.

Wala pa ritong svc.sh. Inilalarawan ito ng dokumentasyon ng GitHub bilang script na “ginagawa pagkatapos matagumpay na maidagdag ang runner,” dahil sinusulat ito mula sa template na may nakapaloob nang repository at pangalan ng runner sa service name. Kaya mabibigo ang sudo ./svc.sh install bago ang ./config.sh at magbabalik ng sudo: ./svc.sh: command not found. I-register muna ang runner, saka i-install ang service.

I-install ang mga dependency ng runner

Ang runner ay isang .NET application, kaya kailangan nito ng ilang shared library. Panatilihin ang shell ng runner user at i-install ang mga ito gamit ang sudo, dahil nagsusulat ang script sa system package database.

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

Sa Ubuntu 24.04, ini-install nito ang libkrb5-3, zlib1g, liblttng-ust1t64, libssl3t64 at libicu74. Sinusubukan ng script ang ilang version name para sa bawat library at ginagamit ang version na kasama sa iyong release. Dahil dito, gumagana ang parehong script sa mas lumang Ubuntu at sa Debian.

Kapag nilaktawan mo ang hakbang na ito, hihinto ang ./config.sh bago ito magsagawa ng anumang operasyon:

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

Ang nawawalang libicu ay nagbibigay ng parehong payo sa ilalim ng ibang unang linya, Libicu's dependencies is missing for Dotnet Core 6.0. Pareho itong nagmumula sa iisang dahilan: isinasagawa ng config.sh ang ldd laban sa mga bundled library bago ito magsimula. Kaya kapag may unresolved link, hihinto ang script sa halip na magdulot ng nakalilitong crash sa bandang huli.

I-register ang runner sa iyong repository

Kumuha ng token mula sa repository. Buksan ang Settings, pagkatapos ay Actions, Runners, at New self-hosted runner. Ipinapakita ng page ang registration token na nagsisimula sa A. Mag-e-expire ito isang oras matapos itong gawin, kaya i-generate ito kapag handa ka nang i-paste.

Mag-register bilang runner user. Tumangging tumakbo ang config.sh kapag nasa ilalim ito ng 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

Narito ang ginagawa ng mga flag. Tinutukoy ng --name kung paano lilitaw ang runner sa repository, kaya pumili ng pangalang makikilala mo pa rin pagkalipas ng anim na buwan. Nagdaragdag ang --labels ng sarili mong labels; awtomatikong mayroon nang self-hosted, Linux at X64 ang runner. Tinutukoy ng --work ang directory kung saan ilalagay ang mga checkout, sa loob ng runner directory. Sinasagot ng --unattended ang mga interactive prompt gamit ang mga default value ng mga ito. Ito ang kailangan kapag nasa script ang command. Inaangkin ng --replace ang dati nang registration na may kaparehong pangalan sa halip na mag-fail. Ito ang kailangan kapag nire-rebuild mo ang server.

Nagtatapos ang matagumpay na pag-run sa mga linyang ito:

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

Nasa runner directory na ngayon ang registration bilang .runner, .credentials at .credentials_rsaparams. Tinutukoy ng huling dalawa ang runner na ito sa GitHub, kaya maaaring gamitin ito para magpanggap na runner ang sinumang makakabasa sa mga ito. Ito ang dahilan kung bakit mode 700 ang directory at walang sudo ang user.

I-install ang runner bilang systemd service

Ang ./run.sh sa terminal ay sapat para sa isang test, pero hihinto ito kapag natapos ang SSH session mo. I-install ang service para awtomatikong magsimula ang runner kapag nag-boot. Ipinapaliwanag ng systemd services at timer sa isang VPS ang mismong unit file. Dito, ang svc.sh ang gagawa nito para sa iyo.

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

Kailangan ng svc.sh ang root dahil nagsusulat ito ng unit sa /etc/systemd/system at ine-enable ito. Ang argument pagkatapos ng install ay ang user na gagamitin ng service. Tiyaking tahasang ipasa ang gharunner. Kung walang argument, gagamitin ng script ang $SUDO_USER, na iyong admin account. Dahil dito, tatakbo ang bawat job bilang user na maaaring gumamit ng sudo.

Pinangalanan ang unit batay sa repository at runner, sa format na actions.runner.YOUR-USER-YOUR-REPO.vps-runner-1.service. Hindi mo kailangang i-type ito nang manu-mano:

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

Ang maayos na runner ay nagla-log ng √ Connected to GitHub, na sinusundan ng linyang nagtatapos sa Listening for Jobs. Ipinapakita rin ito ng Runners page ng repository bilang Idle. Ang runner na may status na Offline ay maaaring hindi tumatakbo o hindi makakonekta sa GitHub sa port 443.

Magpadala ng job sa runner

Pinipili ng runs-on ang runner batay sa label. Humingi ng self-hosted kasama ang sarili mong label upang hindi mapunta ang job sa runner na hindi mo nilayon.

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

Kung naghihintay ang job sa Waiting for a runner to pick up this job, hindi tugma ang mga label. Dapat mayroon sa runner ang bawat label sa runs-on, kaya kahit isang sobrang salita ay mag-iiwan sa job na naka-queue nang walang anumang error. Ihambing ang listahan sa mga label na ipinapakita sa tabi ng runner sa repository settings.

Bakit hindi dapat pagsamahin ang self-hosted runners at public repositories

Ito ang bahaging madalas nilalaktawan. Malinaw ang gabay ng GitHub: ang self-hosted runners ay “halos hindi kailanman dapat gamitin para sa public repositories,” at “walang garantiya na tumatakbo ang mga ito sa ephemeral na malinis na virtual machines, kaya maaari silang patuloy na ma-compromise ng untrusted code sa isang workflow.”

Simple ang mekanismo. Ang pull request mula sa fork ay may sariling kopya ng workflow file. Kung nagpapatakbo ang iyong public repository ng pull request workflows sa iyong runner, maaaring magmungkahi ang sinumang makapag-fork ng repository ng workflow na magpapatakbo ng kanilang commands sa iyong VPS. Hindi nila kailangan ng write access, dahil ang mismong ipinapanukala nila ang siyang tumatakbo.

Napapahina ng approval settings ang panganib na ito, pero hindi nila ito inaayos. Sa default policy para sa public repository, kailangang aprubahan ng isang maintainer ang workflow mula sa fork ng first-time contributor. Kapag naaprubahan mo na ang taong iyon nang isang beses, tatakbo ang mga susunod nilang pull request nang walang panibagong prompt. Kaya ang gate ay isang taong nagbabasa ng diff sa bawat pagkakataon, at madaling hindi mapansin ang payload na nakatago tatlong antas sa loob ng build script.

Hindi tumatanggap ng iyong secrets ang isang fork pull request, at read only ang GITHUB_TOKEN nito. Nililimitahan nito ang pinsala sa loob ng GitHub. Wala itong ginagawa para protektahan ang iyong server. May shell ang attacker bilang gharunner, kaya mababasa nila ang bawat file na may access ang user na iyon, maaabot ang anumang naaabot ng VPS sa private network nito, at maaaring mag-iwan ng backdoor sa ~/.bashrc o sa isang user systemd unit na tatakbo sa susunod na job.

Ang pag-register gamit ang --ephemeral ay nagpapagawa sa runner ng isang job at pagkatapos ay nag-deregister, kaya hindi mababasa ng isang job ang workspace ng susunod na job. Nakakatulong lamang ito kung may nagre-rebuild ng machine o container para sa bawat job, dahil mananatili ang backdoor na isinulat sa home directory ng runner user kahit may bagong registration.

Maikli ang mga sumusunod na panuntunan. Gamitin ang self-hosted runners para sa private repositories. Kung kailangan mong mag-attach ng isa sa isang public repository, huwag magpatakbo rito ng fork pull requests, huwag magpatakbo ng iba pang workload sa server na iyon, at ituring ang machine na disposable.

Mga Docker job, service container, at anumang workflow step na tumatawag sa docker build ay nangangailangan ng Docker daemon sa runner host. I-install ang Docker sa karaniwang paraan, na saklaw ng Docker at Docker Compose sa isang VPS, at idagdag ang runner user sa docker group.

Unawain muna ang kapalit bago ito gawin. Katumbas ng root ang pagiging miyembro ng docker group, dahil maaaring mag-bind mount ang isang container sa / at tumakbo bilang root sa loob nito. Kaya maaaring basahin at isulat ng workflow na kayang kumonekta sa Docker socket ang bawat file sa VPS, kabilang ang /etc/shadow. Sa isang private repository na may mga pinagkakatiwalaang contributor, maaaring katanggap-tanggap ang kapalit na ito. Sa ibang sitwasyon, inaalis nito ang saysay ng paggamit ng unprivileged user. Pinananatili ng Rootless Docker ang container builds sa loob ng sariling permissions ng runner user, kapalit ng mas mabagal na storage driver at kawalan ng privileged containers.

Mga update at malinis na pag-aalis ng runner

Bilang default, awtomatikong nag-a-update ang self-hosted runner. Nakikita nito ang bagong release, pinapalitan ang sarili nitong files, at nire-restart ang service, kaya karaniwan ay wala kang kailangang gawin. Ino-off ng ./config.sh --disableupdate ang self-update kapag kailangan mo ng fixed version. Pagkatapos nito, ikaw na ang responsable sa pag-update: malinaw sa documentation ng GitHub na ang runner na naka-configure gamit ang --disableupdate ay kailangang i-update nang mano-mano.

Napapanatili ng manual na update ang registration dahil wala sa tarball ang .runner at .credentials. I-stop ang service, i-download at i-checksum ang bagong tarball gaya ng nasa gharunner, i-extract ito sa parehong directory gamit ang tar xzf, at pagkatapos ay simulan muli ang service:

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

Para alisin ang runner, i-uninstall muna ang service, saka ito i-deregister. Makukuha ang removal token sa parehong Runners page, sa ilalim ng sariling Remove button ng runner.

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

Kapag dinelete ang directory nang hindi dini-deregister ang runner, mananatili itong nakalista bilang Offline sa repository dahil malalaman lang ng GitHub na wala na ito kapag sinabi mismo ito ng runner o mano-manong dinelete ng admin ang entry.

Mga failure mode at ang mga string na makikita mo

Must not run with sudo. Ipinapakita ito ng config.sh at lumalabas kapag root ang nagpatakbo nito. Sinasadya ang check na ito dahil sisirain ng mga file na pagmamay-ari ng root sa _work ang lahat ng susunod na job na tatakbo bilang service user. Patakbuhin ang ./config.sh bilang gharunner. Ino-override ng variable na RUNNER_ALLOW_RUNASROOT ang check, pero ipinagpapaliban lamang nito ang problema.

sudo: ./svc.sh: command not found. Nasa tamang directory ka. Hindi pa umiiral ang svc.sh dahil hindi pa nakukumpleto ng config.sh ang registration. I-register ang runner, pagkatapos ay i-install ang service.

Http response code: NotFound from 'POST https://api.github.com/actions/runner-registration'. Hindi valid na registration token ang token. Maaaring nag-expire na ito dahil isang oras lamang ang validity nito, o maaaring personal access token ang na-paste sa halip na registration token mula sa Runners page. Bumuo ng bagong token at i-paste itong muli.

Dependencies is missing for Dotnet Core 6.0. Patakbuhin ang sudo ./bin/installdependencies.sh mula sa runner directory bilang root, pagkatapos ay mag-register muli.

Runner Offline pagkatapos ng reboot. Patakbuhin ang systemctl is-enabled 'actions.runner.*'. Kung walang nakalista, hindi kailanman pinatakbo ang ./svc.sh install, kaya umiral lamang ang runner sa loob ng terminal session mo. Kung enabled ang unit at Offline pa rin ang runner, basahin ang journalctl -u 'actions.runner.*' at suriin ang outbound HTTPS.

Napupuno ang disk. Naiipon ang checkouts, build cache, at Docker images sa ilalim ng _work at sa home directory ng runner user, at walang awtomatikong naglilinis ng mga ito. I-monitor ang du -sh /home/gharunner/actions-runner/_work at magdagdag ng naka-schedule na clean-up bago ang disk ang magdesisyon para sa iyo.

FAQ

Bakit sinasabi ng sudo ./svc.sh install na command not found?

Dahil wala ang svc.sh sa runner tarball. Ginagawa ito sa runner directory kapag natapos ng ./config.sh ang registration, gamit ang pangalan ng repository at runner para buuin ang service name. Unang patakbuhin ang ./config.sh bilang runner user. Pagkatapos nito, mahahanap ng sudo ./svc.sh install gharunner ang script at magsusulat ito ng unit na pinangalanang actions.runner.OWNER-REPO.RUNNER-NAME.service sa /etc/systemd/system.

Kailangan ko bang magbukas ng firewall port para sa self-hosted runner?

Hindi. Nagbubukas ang runner ng outbound HTTPS connection papunta sa GitHub at pinananatili itong bukas habang naghihintay ng mga job. Kaya hindi kailanman nagsisimula ang GitHub ng connection papunta sa iyong VPS. Payagan ang outbound 443 at panatilihing sarado ang inbound rules. Kung ipinapakita ng runner ang Offline kahit tumatakbo ang service nito, suriin ang outbound filtering at DNS, hindi ang inbound rules.

Maaari ba akong gumamit ng self-hosted runner sa public repository?

Maaari, pero hindi ito inirerekomenda ng GitHub. May sarili nitong workflow file ang pull request mula sa fork. Kaya maaaring magmungkahi ng mga command na tatakbo sa iyong machine ang sinumang makapag-fork ng iyong repository. Ang approval prompt ay para lamang sa unang run ng isang contributor. Kung mag-a-attach ka ng runner sa isang public repository, i-disable ang fork pull request workflows dito, huwag magpatakbo ng iba pang workload sa server na iyon, at regular na i-rebuild ang machine.

Bakit nabibigo ang registration gamit ang Http response code: NotFound?

Nagbabalik ang registration call ng NotFound kapag mali ang credential, hindi lamang kapag mali ang URL. Dahil dito, maaaring mapanlinlang ang mensahe. Nag-e-expire ang registration token isang oras matapos itong ipakita, at hindi tinatanggap ang personal access token para sa call na ito. Buksan muli ang Settings, Actions, Runners, New self-hosted runner, kopyahin ang bagong token, at tiyaking tumutukoy ang value ng --url sa repository kung saan mayroon kang admin rights.

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