SSD Nodes Learn 8GB RAM — $66/taon
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-02

Self-hosted GitHub Actions runner sa Ubuntu VPS

Alamin kung paano mag-register ng self-hosted GitHub Actions runner sa Ubuntu 24.04 gamit ang dedicated user, checksum, config.sh, systemd service, at fork PR risk.

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 sa GitHub at pinapatakbo ang mga ito sa sarili mong hardware. Nirerehistro mo ito sa isang repository, ini-install bilang systemd service, at awtomatiko itong bumabalik pagkatapos ng bawat reboot. Si GitHub ang nag-iiskedyul ng job. Ang server mo ang gumagawa ng trabaho.

Mahalaga ang CI (continuous integration) sa sarili mong server sa dalawang dahilan. Hindi na sinisingil batay sa oras 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. Ipinapatupad 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 ng workflow file. Sa public repository, may tunay itong panganib. Ipinapaliwanag ng seksiyon tungkol sa fork pull request 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, gaya ng kalagayang makakamit mo sa unang sampung minuto sa isang 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 ito ng trabaho, kaya hindi kailanman kumokonekta ang GitHub sa server mo. Maaaring manatiling sarado sa mundo ang firewall mo at makakarating pa rin ang mga job.

Kailangan mo rin ng admin rights sa repository, dahil ipinapakita ang registration token sa mga setting ng repository.

Gumawa ng dedicated user para sa runner

Huwag 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 magagamit ng runner user ang sudo. Gumawa ng isang unprivileged user na walang pagmamay-ari maliban sa sarili nitong home directory. Saklaw ng Mga least privilege user account sa isang VPS ang pangkalahatang pattern. Ito 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 ang password na iyon. Mahalaga ang Mode 700 sa runner directory dahil iniimbak ng runner ang mga credential nito roon bilang cleartext, at maaaring maglaman ang checkout ng pribadong 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 nangangahulugang naka-lock ang password ang L. Dapat tumugon ang sudo -l -U gharunner ng is not allowed to run sudo. Kung magpi-print ito ng listahan ng mga pinapahintulutang command, nasa sudo group ang account at nawala ang isolation na kakagawa mo lang.

I-download ang runner at suriin ang tarball

Gamitin ang runner user mula rito.

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 file na linux-x64 sa itaas. Ginagamit ng aarch64 ang actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz.

Ngayon, i-verify 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 ito 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 binagong 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 makakita ng problema. Ang archive na hindi kumpletong naisulat 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 pagkakasulat o napalitan ang file.

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

Ano ang nilalaman ng tarball at ano ang wala rito

Pagkatapos ng extraction, nasa directory ang config.sh, run.sh, env.sh, safe_sleep.sh, bin/ at externals/. Nasa bin/ ang runner binaries at bin/installdependencies.sh. Nasa externals/ ang bundled Node runtime na ginagamit ng JavaScript actions.

Wala pa ang svc.sh. Inilalarawan ito ng documentation ng GitHub bilang script na “ginagawa pagkatapos matagumpay na maidagdag ang runner,” dahil ginagawa ito mula sa template na may nakatakdang pangalan ng repository at runner sa service name. Kaya mabibigo ang sudo ./svc.sh install bago ang ./config.sh dahil sa sudo: ./svc.sh: command not found. Mag-register muna, pagkatapos ay i-install ang service.

I-install ang mga dependency ng runner

Ang runner ay isang .NET application, kaya nangangailangan ito ng ilang shared library. Panatilihin ang shell ng runner user at i-install ang mga ito gamit ang sudo, dahil isinusulat ng script ang 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 pinananatili ang pangalan 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, humihinto 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 ang pinagmumulan ng mga ito: pinapatakbo ng config.sh ang ldd laban sa mga bundled library bago ito magsimula, kaya humihinto ang script kapag may unresolved link sa halip na magdulot ng nakalilitong crash sa bandang huli.

Irehistro 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. Nag-e-expire ito isang oras matapos itong malikha, kaya gumawa nito kapag handa ka nang i-paste ito.

Magrehistro bilang runner user. Tumangging tumakbo ang config.sh sa ilalim 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

Ganito gumagana ang mga flag na iyon. Tinutukoy ng --name kung paano lalabas 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 may self-hosted, Linux at X64 na 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 kanilang mga default, na angkop kapag nasa script ang command. Kinukuha ng --replace ang kasalukuyang registration na may parehong pangalan sa halip na mabigo, na angkop kapag muli mong ginagawa ang server.

Nagtatapos ang matagumpay na pagpapatakbo sa mga linyang ito:

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

Nasa runner directory na ang registration bilang .runner, .credentials at .credentials_rsaparams. Tinutukoy ng huling dalawang ito ang runner na ito sa GitHub, kaya maaaring magpanggap bilang 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 humihinto ito kapag natapos ang SSH session mo. I-install ang service para awtomatikong magsimula ang runner sa boot. Ipinapaliwanag ng systemd services at timers sa isang VPS ang mismong unit files. 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

Nangangailangan ng root ang svc.sh 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. Ipasa nang tahasan ang gharunner. Kapag walang argument, gagamitin ng script ang $SUDO_USER, na iyong admin account, kaya tatakbo ang bawat job bilang user na maaaring gumamit ng sudo.

Pinapangalanan ang unit batay sa repository at runner, sa anyong actions.runner.YOUR-USER-YOUR-REPO.vps-runner-1.service. Hindi mo kailangang i-type iyon:

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

Ang isang gumaganang runner ay nagla-log ng √ Connected to GitHub at pagkatapos ay ng linyang nagtatapos sa Listening for Jobs, at ipinapakita ito ng Runners page ng repository bilang Idle. Ang runner na ipinapakitang Offline ay maaaring hindi tumatakbo o hindi makakonekta sa GitHub sa port 443.

Magpadala ng job sa runner

Pinipili ng runs-on ang isang runner batay sa label. Hilingin ang self-hosted at 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 umiiral sa runner ang bawat label sa runs-on, kaya ang 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 mga self-hosted runner at public repository

Ito ang bahaging madalas nilalampasan. Malinaw ang gabay ng GitHub: ang mga self-hosted runner ay “halos hindi dapat gamitin para sa mga public repository,” at “walang garantiya na tatakbo ang mga ito sa mga pansamantala at malilinis na virtual machine, at maaaring patuloy na ma-breach ng hindi pinagkakatiwalaang 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 mga pull request workflow sa iyong runner, maaaring magmungkahi ang sinumang makapag-fork ng repository ng workflow na nagpapatakbo ng kanilang mga command sa iyong VPS. Hindi nila kailangan ng write access, dahil ang iminumungkahi nila ang mismong tumatakbo.

Napapahina ng approval settings ang panganib na ito, pero hindi nila ito nalulutas. Bilang default, hinihingi ng policy para sa isang public repository sa maintainer na aprubahan ang fork workflow ng unang beses na contributor. Pagkatapos mong aprubahan ang taong iyon nang isang beses, tatakbo ang mga susunod nilang pull request nang walang bagong 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 nakatatanggap ang pull request mula sa fork ng iyong secrets, at read only ang GITHUB_TOKEN nito. Nililimitahan nito ang pinsala sa loob ng GitHub. Wala itong ginagawa para sa iyong server. May shell ang attacker bilang gharunner, kaya mababasa nila ang bawat file na maaaring basahin ng user na iyon, maaabot ang anumang maaaring maabot ng VPS sa private network nito, at makapag-iiwan ng backdoor sa ~/.bashrc o sa isang user systemd unit na tatakbo sa susunod na job.

Kapag nag-register gamit ang --ephemeral, tatanggap ang runner ng isang job at pagkatapos ay mag-deregister, kaya hindi mababasa ng isang job ang workspace ng susunod na job. Makakatulong 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 magkaroon ng bagong registration.

Maikli ang mga sumusunod na panuntunan. Gumamit ng self-hosted runner para sa mga private repository. Kung kailangan mong mag-attach ng isa sa isang public repository, huwag magpatakbo rito ng mga pull request mula sa fork, huwag magpatakbo ng iba pang bagay sa server na iyon, at ituring na disposable ang machine.

Docker jobs at ang grupong katumbas ng root

Kailangan ng container jobs, service containers, at anumang workflow step na tumatawag sa docker build ang Docker daemon sa runner host. I-install ang Docker sa karaniwang paraan, na saklaw ng Docker at Docker Compose sa isang VPS, pagkatapos ay idagdag ang runner user sa grupong docker.

Unawain muna ang kapalit bago mo ito gawin. Ang pagiging miyembro ng grupong docker ay katumbas ng root, dahil maaaring mag-bind mount ang isang container ng / at tumakbo bilang root sa loob nito. Kaya maaaring basahin at isulat ng workflow na may access sa Docker socket ang bawat file sa VPS, kasama ang /etc/shadow. Sa isang private repository na may mga contributor na pinagkakatiwalaan, maaaring katanggap-tanggap ang kapalit na ito. Sa ibang sitwasyon, inaalis nito ang layunin ng paggamit ng unprivileged user. Pinapanatili ng Rootless Docker ang container builds sa loob ng sariling mga pahintulot ng runner user, ngunit mas mabagal ang storage driver at walang privileged containers.

Mga update at malinis na pag-aalis ng runner

Awtomatikong nag-u-update ang isang self-hosted runner bilang default. Nakikita nito ang bagong release, pinapalitan ang sarili nitong mga file, 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 kailangang mag-update: malinaw sa documentation ng GitHub na kailangang manu-manong i-update ang runner na naka-configure gamit ang --disableupdate.

Napapanatili ng manual na update ang registration dahil wala sa tarball ang .runner at .credentials. Ihinto ang service, i-download at i-checksum ang bagong tarball bilang 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, pagkatapos ay alisin ang registration nito. Ang removal token ay mula 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 inaalis ang registration, mananatiling nakalista ang runner bilang Offline sa repository dahil malalaman lamang ng GitHub na wala na ito kapag ipinabatid ito ng runner o kapag mano-manong dinelete ng admin ang entry.

Mga failure mode, kasama ang mga string na makikita mo

Must not run with sudo. Ipi-print ito ng config.sh at lalabas kapag pinatakbo bilang root. Sinasadya ang check na ito, dahil sinisira ng mga file na pagmamay-ari ng root sa _work ang lahat ng susunod na job na tumatakbo bilang service user. Patakbuhin ang ./config.sh bilang gharunner. Ino-override ng variable na RUNNER_ALLOW_RUNASROOT ang check, pero ipinagpapaliban lamang nito ang failure.

sudo: ./svc.sh: command not found. Nasa tamang directory ka. Hindi pa umiiral ang svc.sh dahil hindi pa nakakumpleto ng registration ang config.sh. 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 ito dahil isang oras lamang ang validity nito, o personal access token ang nai-paste kapalit ng 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 naka-enable ang unit at Offline pa rin ang runner, basahin ang journalctl -u 'actions.runner.*' at suriin ang outbound HTTPS.

Napupuno ang disk. Naiipon ang mga checkout, build cache at Docker image 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 nakaiskedyul na cleanup bago disk space ang maubos.

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 mo para buuin ang pangalan ng service. Patakbuhin muna ang ./config.sh bilang runner user. Pagkatapos nito, mahahanap ng sudo ./svc.sh install gharunner ang script at maisusulat nito ang 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 sa GitHub at pinananatili itong bukas habang naghihintay ito ng mga job. Kaya hindi kailanman sinisimulan ng GitHub ang connection papunta sa iyong VPS. Payagan ang outbound 443 at panatilihing sarado ang inbound rules. Kung ipinapakita ng runner na Offline habang tumatakbo ang service nito, suriin ang outbound filtering at DNS, hindi ang inbound rules.

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

Maaari, pero hindi ito inirerekomenda ng GitHub. May sariling workflow file ang pull request mula sa fork. Kaya ang sinumang maaaring mag-fork ng iyong repository ay maaaring magmungkahi ng mga command na tatakbo sa iyong machine. Ang approval prompt ay para lamang sa unang run ng isang contributor. Kung mag-aattach ka ng runner sa isang public repository, i-disable ang fork pull request workflows dito, huwag mag-iwan ng ibang bagay 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 makapanlinlang 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 isang repository kung saan mayroon kang admin rights.

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