HostiServer
2026-08-13 09:47
GitHub Actions self-hosted runner: instalace a první pipeline na VPS
📚 Série „CI/CD od nuly", část 3 ze 6:
- Co je CI/CD: od ručního nasazení k automatizovaným pipeline
- Self-hosted CI/CD: koncept, architektura a runnery
- GitHub Actions self-hosted runner: instalace a první pipeline na VPS ← jste zde
- GitLab CI self-hosted runner: instalace a první pipeline na VPS
- Gitea Actions: Git platforma a pipeline na jednom serveru
- GitHub Actions vs GitLab CI vs Gitea Actions v roce 2026: co zvolit pro self-hosted CI/CD
GitHub Actions self-hosted runner: instalace a první pipeline na VPS
Ve druhé části jsme rozebrali, jak je self-hosted runner uspořádaný: agent si vyzvedne úlohu z fronty platformy, provede ji na vašem serveru a vrátí logy a stav. Rozebrali jsme také, proč se agenta vyplatí přesunout na vlastní hardware a jak izolovat provádění kódu. Nyní se stejná architektura promění v konkrétní příkazy: vezmeme VPS, připojíme ho k repozitáři na GitHubu a dotáhneme celý řetězec až k funkčnímu nasazení do produkce.
Tato část je od začátku do konce praktická. Nejprve stručně o tom, jak je GitHub Actions poskládán, aby dál YAML dával smysl a nekopíroval se naslepo. Pak instalace agenta, zapouzdření jako systemd služby, první workflow, práce se secrets a kompletní pipeline, která po push do main sestaví projekt a nasadí ho na server přes SSH.
1.1 Co už máme a co bude dál
Minimum, ze kterého vycházíme:
- repozitář na GitHubu, soukromý nebo interní pro organizaci;
- VPS s Ubuntu 22.04 nebo 24.04, přístup přes SSH, samostatný uživatel bez práv root;
- administrátorská práva k repozitáři: bez nich není stránka pro nastavení runnerů dostupná.
Agent nepotřebuje veřejnou adresu ani otevřené vstupní porty. Sám naváže spojení s GitHubem přes port 443 a drží ho, přičemž čeká na úlohu. To je důležitá vlastnost: runner lze umístit uvnitř uzavřené sítě, kam zvenčí není přístup vůbec, a přesto bude fungovat úplně stejně.
1.2 GitHub Actions: z čeho se skládá ekosystém
GitHub Actions je automatizační systém zabudovaný v GitHubu, spuštěný v roce 2019. Konfigurace žije přímo v repozitáři, v adresáři .github/workflows/: každý YAML soubor je samostatný workflow, který GitHub automaticky rozpozná bez jakékoli registrace v rozhraní.
Druhou částí ekosystému je Marketplace hotových akcí. Akce (action) je znovupoužitelný krok zabalený jako samostatný repozitář: actions/checkout naklonuje kód, actions/setup-node nainstaluje potřebnou verzi Node.js, docker/build-push-action sestaví a publikuje image. Místo desítky řádků bash v konfiguraci se objeví jeden řádek uses.
Pohodlí má i druhou stranu mince. Každá cizí akce je cizí kód, který se provádí ve vašem prostředí a vidí vaše secrets. Na self-hosted runneru je cena chyby vyšší než na jednorázovém cloudovém stroji: tento server vám žije dál. Praktické pravidlo je přišpendlit akce na plný commit hash místo tagu, přinejmenším u všeho, co nepatří organizacím actions a github:
# místo pohyblivého tagu
- uses: some-org/some-action@v3
# přišpendleno na konkrétní commit
- uses: some-org/some-action@8f4b7e2c9a1d3f5b6c8e0a2d4f6b8c0e2a4d6f8b # v3.1.0
ℹ️ Terminologie: GitHub Actions nemá stages jako samostatnou entitu, jako je má GitLab CI. Pořadí provádění se určuje závislostmi přes klíč needs. Pokud přicházíte z GitLabu, jde o hlavní rozdíl, na který si musíte zvyknout.
2. Architektura GitHub Actions
Před instalací agenta se vyplatí rozebrat tři úrovně, ve kterých se práce popisuje. Veškerý YAML dále v článku se opírá právě o ně.
2.1 Workflow → Job → Step
Workflow je jeden YAML soubor v .github/workflows/. Odpovídá na otázku „kdy se spustit" a „co udělat". Souborů může být libovolné množství: samostatné kontroly pro pull request, samostatné noční skenování závislostí, samostatný release podle tagu.
Job je úloha uvnitř workflow. Každá úloha dostane vlastní runner a vlastní čisté prostředí. Úlohy se ve výchozím stavu provádějí paralelně, pořadí se určuje přes needs. Důležitý důsledek: dvě úlohy nevidí navzájem své soubory, výměna probíhá přes artefakty.
Step je samostatný krok uvnitř úlohy. Kroky se provádějí postupně v jednom prostředí, takže soubory se mezi nimi předávají běžným způsobem. Krok existuje ve dvou typech: run pro shellové příkazy a uses pro hotovou akci.
workflow (soubor ci.yml)
├── job: build → runner č. 1, vlastní prostředí
│ ├── step: checkout
│ ├── step: npm ci
│ └── step: npm run build
└── job: deploy → runner č. 2, čisté prostředí
needs: build
├── step: download-artifact
└── step: ssh deploy
2.2 Triggery: kdy se workflow spustí
Triggery se popisují v sekci on. Čtyři hlavní:
- push do určité větve nebo podle tagu. Základní trigger pro CI i pro release.
- pull_request. Kontroluje výsledek sloučení ještě předtím, než k němu skutečně dojde. Právě na tento trigger se věší blokování sloučení, dokud kontroly neprojdou.
- schedule. Spuštění podle rozvrhu v cron syntaxi, v časovém pásmu UTC. Vhodné pro noční běhy a denní skenování závislostí.
- workflow_dispatch. Spuštění tlačítkem v rozhraní nebo voláním přes API, s volitelnými parametry. Je to ten samý ruční krok, který odlišuje Continuous Delivery od Continuous Deployment.
on:
push:
branches: [main]
paths-ignore:
- '**.md'
- 'docs/**'
pull_request:
branches: [main]
schedule:
- cron: '0 3 * * *' # denně v 03:00 UTC
workflow_dispatch:
inputs:
environment:
description: 'Kam nasadit'
type: choice
options: [staging, production]
default: staging
K rozvrhu existuje nuance, která ušetří hodiny pátrání: schedule funguje jen pro workflow ve výchozí větvi a spuštění se může opozdit o pár minut nebo posunout kvůli špičkovému zatížení. Pro přesný čas je potřeba externí plánovač, který přes API dere za workflow_dispatch.
2.3 GitHub-hosted a self-hosted runner: rozdíl v praxi
GitHub-hosted runner je virtuální stroj, který GitHub vytvoří pro každou úlohu a po ní zničí. Self-hosted runner je váš vlastní server s nainstalovaným agentem, který žije trvale. Rozdíl jde daleko za hranici toho, kdo platí za hardware.
| Parametr | GitHub-hosted | Self-hosted |
|---|---|---|
| Životnost prostředí | Čistý VM pro každou úlohu | Trvalé, stav se hromadí |
| Zdroje | Fixní podle tarifu | Vaše, klidně GPU a stovky GB RAM |
| Platba | Za minuty sestavení | Za server, minuty se nepočítají |
| Předinstalovaný software | Velký image s jazyky a utilitami | Jen to, co jste nainstalovali sami |
| Přístup do uzavřené sítě | Není | Je, runner stojí uvnitř |
| Fronta ve špičce | Možná | Vlastní fronta, předvídatelný čas |
| Administrace | Žádná | Aktualizace, disk, izolace, monitoring |
Řádek o trvalém prostředí je zároveň hlavní výhodou i hlavní pastí. Výhoda je v rychlosti: mezi sestaveními zůstává cache balíčků, vrstvy Dockeru, naklonovaný repozitář, takže druhé sestavení bývá často dvakrát rychlejší než první. Past je v tom, že zůstává i všechno ostatní: globálně nainstalované balíčky, dočasné soubory, zapomenuté proměnné prostředí, soukromý klíč, který předchozí úloha zapsala do domovského adresáře a zapomněla uklidit.
⚠️ Veřejné repozitáře: GitHub přímo nedoporučuje připojovat self-hosted runner k veřejnému repozitáři. Kdokoli může otevřít pull request a spolu s ním navrhnout změny ve workflow nebo v kódu, který tento workflow provádí. Na jednorázovém cloudovém stroji to skončí spolu s úlohou. Na vašem serveru získá cizí kód přístup ke všemu, co na něm leží, i k síti, ve které stojí. Pro open source zůstaňte u GitHub-hosted runnerů.
3. Instalace self-hosted runneru na VPS
Dál následuje postup, po kterém se agent objeví v seznamu runnerů se stavem Idle a přežije restart serveru.
3.1 Registrace na GitHubu a získání tokenu
Runner se připojuje na jedné ze tří úrovní:
- Repozitář. Settings → Actions → Runners → New self-hosted runner. Nejjednodušší varianta pro prvního agenta.
- Organizace. Stejná cesta v nastavení organizace. Jeden agent obsluhuje více repozitářů, přístup se odděluje skupinami runnerů.
- Enterprise. Úroveň pro velké instalace, stejná logika.
Stránka rovnou zobrazí hotové příkazy pro zvolený OS a architekturu, s aktuální verzí agenta a kontrolním součtem. Kopírujte je odtamtud: verze se pravidelně aktualizuje a příkazy z cizího článku rychle zastarají.
⚠️ Registrační token žije zhruba hodinu. Pokud mezi zkopírováním a spuštěním config.sh uplyne víc času, konfigurace spadne s chybou autorizace. Řešení je jednoduché: obnovit stránku a vzít nový token. Tento token je potřeba jen pro připojení agenta, pro běžný provoz se nepoužívá.
3.2 Stažení a spuštění agenta
Všechny příkazy se provádějí pod běžným uživatelem, ne pod root. Agent se odmítne nakonfigurovat pod root, a to je správné chování: úlohy se provádějí s právy toho uživatele, pod kterým agent běží.
# samostatný systémový uživatel pro runner
sudo adduser --disabled-password --gecos "" runner
sudo usermod -aG docker runner # jen pokud sestavení používají Docker
sudo su - runner
mkdir -p ~/actions-runner && cd ~/actions-runner
# verzi a odkaz vezmeme ze stránky New self-hosted runner
RUNNER_VERSION=2.3xx.x
curl -o actions-runner.tar.gz -L \
https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz
# kontrola kontrolního součtu s tím, který ukázal GitHub
echo "<hash ze stránky> actions-runner.tar.gz" | shasum -a 256 -c
tar xzf ./actions-runner.tar.gz
Rozbalený adresář obsahuje config.sh pro připojení, run.sh pro spuštění, svc.sh pro zapouzdření jako služby a skript bin/installdependencies.sh, který doručí systémové knihovny potřebné pro .NET prostředí agenta. Poslední se spouští jednou a vyžaduje sudo.
sudo ./bin/installdependencies.sh
./config.sh \
--url https://github.com/OWNER/REPO \
--token <token ze stránky> \
--name vps-build-01 \
--labels self-hosted,linux,x64,build \
--work _work \
--unattended \
--replace
Co znamenají přepínače:
--nameje jméno v seznamu runnerů. Dělejte ho smysluplné: až budete mít pět agentů,vps-build-01se čte lépe nežubuntu-server.--labelsjsou štítky, podle kterých workflow vybírá agenta. Ke třem standardním (self-hosted, OS, architektura) přidávejte vlastní:build,deploy,gpu.--unattendedodstraní interaktivní dotazy, což je potřeba pro automatizaci přes Ansible nebo cloud-init.--replacepřepíše agenta se stejným jménem, pokud je už zaregistrovaný.
Spojení lze ověřit hned, na popředí:
./run.sh
# √ Connected to GitHub
# Listening for Jobs
V tuto chvíli se agent objeví v seznamu se stavem Idle. Zastavíme ho přes Ctrl+C a přejdeme ke službě.
3.3 Runner jako systemd služba
Spuštění přes run.sh žije přesně do zavření session. Pro trvalý provoz se agent zapouzdří jako služba a skript pro to už leží v adresáři:
sudo ./svc.sh install runner
sudo ./svc.sh start
sudo ./svc.sh status
Skript vytvoří unit s názvem typu actions.runner.OWNER-REPO.vps-build-01.service a zapne automatické spouštění. Dál se s ním pracuje běžnými prostředky systemd:
systemctl status 'actions.runner.*'
journalctl -u 'actions.runner.*' -f
sudo ./svc.sh stop
sudo ./svc.sh uninstall
Podrobné logy provádění úloh leží samostatně, v adresáři _diag uvnitř složky agenta. Vyplatí se tam nahlédnout, když úloha spadne bez srozumitelné zprávy v rozhraní GitHubu.
Dvě nastavení, která je lepší udělat hned:
- Rotace pracovního adresáře. Adresář
_workroste bez konce: každý repozitář, každý artefakt, každá cache. Jednou týdně ho čistěte podle rozvrhu nebo sledujte volné místo, protože docházející disk vypadá jako chaotické pády sestavení. - Automatické aktualizace. Agent se aktualizuje sám, jakmile GitHub vydá novou verzi. Pokud aktualizace mají probíhat jen ve vašem servisním okně, přidejte při konfiguraci
--disableupdatea aktualizujte ručně. Počítejte s tím, že zastaralý agent časem přestane přijímat úlohy.
4. První workflow
Agent je připojený, teď mu dáme práci.
4.1 Struktura YAML souboru
Soubor se ukládá do .github/workflows/ci.yml a má tři povinné části: name pro zobrazení v rozhraní, on pro triggery, jobs pro samotnou práci.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: [self-hosted, linux, x64]
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
GitHub soubor automaticky rozpozná po push. Nikde ho není třeba registrovat a syntaktické chyby jsou vidět na záložce Actions hned po commitu.
4.2 runs-on: jak úloha najde vašeho agenta
runs-on je filtr podle štítků. Úloha půjde na agenta, který má všechny vyjmenované štítky:
# kterýkoli váš agent
runs-on: self-hosted
# jen linux x64 mezi vašimi
runs-on: [self-hosted, linux, x64]
# jen agent se štítkem gpu
runs-on: [self-hosted, linux, gpu]
# cloudový agent GitHubu
runs-on: ubuntu-latest
Pokud agent s potřebnou sadou štítků neexistuje nebo je offline, úloha nespadne, ale zařadí se do fronty a čeká. Ve výchozím stavu tam visí několik hodin, pak se zruší timeoutem. Toto je typická příčina situace „pipeline se spustil a nic se neděje": nekontrolujte logy, ale shodu štítků a stav agenta.
Štítky se vyplatí kombinovat se skupinami runnerů na úrovni organizace: skupina určuje, které repozitáře vůbec smí posílat úlohy na tohoto agenta, a štítky pak rozdělují úlohy v rámci povoleného.
4.3 Praktický příklad: checkout, závislosti, testy
Kompletní konfigurace s komentáři ke každému bloku:
name: CI
on:
push:
branches: [main]
pull_request:
# nové spuštění pro stejnou větev zruší předchozí
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: [self-hosted, linux, x64]
timeout-minutes: 20
steps:
- name: Získat kód
uses: actions/checkout@v4
- name: Nastavit Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Nainstalovat závislosti
run: npm ci
- name: Linter
run: npm run lint
- name: Testy
run: npm test -- --ci
- name: Uložit report
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report
path: reports/
retention-days: 7
Tři detaily, které jsou na self-hosted runneru důležitější než na cloudovém:
timeout-minutes. Bez něj zaseklá úloha zabírá agenta na šest hodin a všechna další sestavení stojí ve frontě. Na vlastním agentovi to blokuje celý tým, protože náhradní stroj není.concurrency. Ruší předchozí spuštění stejné větve. Když je agent jeden, odstraní to zbytečné běhy pro commity, které jsou už překryté novějšími.if: always(). Krok se provede i po pádu předchozích. Pro uložení reportů je to nutné, protože zajímavý je právě report o neúspěšném běhu.
ℹ️ K setup-node a podobným akcím: na self-hosted runneru si cachují nainstalované verze v adresáři agenta, takže první spuštění je delší a další už téměř okamžité. Alternativou je nainstalovat potřebné verze do systému předem, ale pak se aktualizace verze přestane rovnat změně jednoho řádku v konfiguraci a vrátí se k ruční práci na serveru.
5. Secrets a proměnné
Pipeline téměř vždy potřebuje něco, co nelze uložit do repozitáře: SSH klíče, tokeny registru image, hesla do databáze. GitHub má pro to samostatné úložiště.
5.1 Kde se secrets ukládají
Cesta v rozhraní: Settings → Secrets and variables → Actions. Jsou tam dvě záložky. V Secrets leží hodnoty, které se v logách maskují a po uložení se zpětně nedají přečíst. Ve Variables leží běžná nastavení bez maskování: název větve, adresa staging serveru, verze image.
Secrets existují na třech úrovních:
| Úroveň | Kdo vidí | Kdy zvolit |
|---|---|---|
| Repository | Všechny workflow tohoto repozitáře | Hodnota potřebná jen tomuto projektu |
| Organization | Vybrané repozitáře organizace | Sdílený token registru, klíč monitoringu |
| Environment | Jen úlohy s odpovídajícím environment |
Produkční přístupy, které je třeba oddělit od zbytku |
Maskování funguje na úrovni textu: pokud se hodnota secretu objeví ve výstupu, GitHub ji nahradí hvězdičkami. Ochrana není absolutní. Secret rozebraný na části, zakódovaný do base64 nebo vypsaný po znacích je v logách vidět celý. Kromě maskování proto platí běžné pravidlo minimálních práv: klíč, který umí jen nahrát soubory do adresáře a restartovat jednu službu, se nestane katastrofou ani po úniku.
5.2 Environment secrets pro staging a production
Environments (Settings → Environments) je nejužitečnější mechanismus v této kapitole. Dělá dvě věci najednou: drží vlastní sadu secrets a ukládá pravidla úlohám, které se na něj odkazují.
- Required reviewers. Úloha se zastaví a čeká na potvrzení od konkrétní osoby. Je to ruční tlačítko Continuous Delivery, ale se jménem odpovědného v logách.
- Deployment branches. Omezení, ze kterých větví lze nasazovat. Nastavte
mainpro production a náhodný deploy z experimentální větve se stane nemožným. - Wait timer. Pauza před provedením, během které lze deploy zrušit.
jobs:
deploy-staging:
runs-on: [self-hosted, linux, deploy]
environment: staging
steps:
- run: ./scripts/deploy.sh
env:
HOST: ${{ secrets.DEPLOY_HOST }} # hodnota z prostředí staging
deploy-production:
needs: deploy-staging
runs-on: [self-hosted, linux, deploy]
environment: production # čeká na potvrzení revizora
steps:
- run: ./scripts/deploy.sh
env:
HOST: ${{ secrets.DEPLOY_HOST }} # stejný název, jiná hodnota
Stejný název secretu ve dvou prostředích není náhoda, ale pohodlí: workflow zůstává jeden a rozdíl mezi prostředími drží GitHub.
5.3 Použití secrets ve workflow
Přístup probíhá přes kontext secrets:
steps:
- name: Správně: přes env
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: ./scripts/publish.sh
- name: Nebezpečně: hodnota se dostane do příkazové řádky
run: ./scripts/publish.sh --token ${{ secrets.API_TOKEN }}
Rozdíl mezi dvěma kroky není kosmetický. Ve druhém případě se hodnota dosadí do řetězce příkazu ještě před spuštěním shellu, takže je vidět v ps na serveru a dostane se do historie shellu. Přes env se předává jako proměnná prostředí procesu, a to je znatelně užší plocha.
⚠️ Pull requesty z forků: pro takové běhy nejsou secrets ve výchozím stavu dostupné, a to je ochrana proti zjevnému útoku. Trigger pull_request_target tuto ochranu odstraní, protože provádí workflow v kontextu základní větve včetně secrets. Používejte ho jen s kódem základní větve a nikdy v něm nedělejte checkout kódu z forku.
6. Praktický příklad: deploy na server přes SSH
Dáme to všechno dohromady. Cíl: po push do main se projekt sestaví na runneru, artefakt odjede na produkční server, služba se restartuje.
6.1 Klíč pro deploy
Klíč se generuje samostatný, jen pro tuto úlohu, a nikdy se nepřekrývá s osobním klíčem inženýra:
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ./gh_deploy -N ""
Na produkčním serveru vytvoříme uživatele bez práv root a přidáme mu veřejnou část s omezeními:
# na produkčním serveru
sudo adduser --disabled-password --gecos "" deploy
sudo install -o deploy -g deploy -m 700 -d /home/deploy/.ssh
# /home/deploy/.ssh/authorized_keys
from="203.0.113.10",no-agent-forwarding,no-port-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAA... github-actions-deploy
Parametr from naváže klíč na adresu vašeho runneru: i s ukradeným soukromým klíčem se z jiného stroje přihlásit nepodaří. Práva na restart služby se udělují cíleně, přes sudoers, a ne plošným přístupem k sudo:
# /etc/sudoers.d/deploy
deploy ALL=(root) NOPASSWD: /bin/systemctl restart app.service
6.2 Secrets v repozitáři
Přidáme tři hodnoty do prostředí production:
SSH_PRIVATE_KEYje celý obsah souborugh_deploy, včetně řádkůBEGINaENDa koncového odřádkování.SSH_KNOWN_HOSTSje výstupssh-keyscan -H your-server.example.com.DEPLOY_HOSTje adresa serveru.
Druhý bod se často vynechává a nahrazuje se StrictHostKeyChecking=no. Takhle to dělat nevyplatí: tato volba vypíná kontrolu, která chrání před podvržením serveru, a její přítomnost v konfiguraci znamená, že deploy pojede kamkoli, kde odpoví přes SSH.
6.3 Kompletní workflow
name: Build and deploy
on:
push:
branches: [main]
concurrency:
group: deploy-production
cancel-in-progress: false
jobs:
build:
runs-on: [self-hosted, linux, x64, build]
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test -- --ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
deploy:
needs: build
runs-on: [self-hosted, linux, deploy]
environment: production
timeout-minutes: 10
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Připravit SSH
run: |
install -m 700 -d ~/.ssh
install -m 600 /dev/null ~/.ssh/deploy_key
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
- name: Nahrát soubory
run: |
rsync -az --delete \
-e "ssh -i ~/.ssh/deploy_key" \
dist/ deploy@${{ secrets.DEPLOY_HOST }}:/var/www/app/
- name: Restartovat službu
run: |
ssh -i ~/.ssh/deploy_key deploy@${{ secrets.DEPLOY_HOST }} \
"sudo systemctl restart app.service"
- name: Ověřit, že služba naběhla
run: |
for i in $(seq 1 10); do
code=$(curl -s -o /dev/null -w "%{http_code}" https://app.example.com/health || true)
[ "$code" = "200" ] && exit 0
sleep 3
done
echo "Služba po deployi neodpovídá"
exit 1
- name: Uklidit klíč
if: always()
run: rm -f ~/.ssh/deploy_key
Poslední krok s if: always() není formalita, ale přímý důsledek trvalého prostředí. Na cloudovém runneru stroj zmizí spolu s klíčem. Váš agent žije dál a soukromý klíč zapomenutý v domovském adresáři zůstane dostupný jakékoli další úloze, včetně té, která přijde z jiné větve.
Krok s kontrolou zdraví promění deploy na akci s ověřeným výsledkem. Bez něj workflow zezelená hned po restartu služby, i kdyby aplikace okamžitě spadla do crash-loopu, a o havárii se dozvíte od zákazníků.
ℹ️ Kdy SSH vůbec není potřeba: pokud runner stojí na stejném serveru jako aplikace, deploy se zredukuje na lokální kopírování souborů. Pokušení je pochopitelné, ale sestavení je zátěž na CPU, disk a paměť a bude konkurovat produkci v nejhorší možný moment. Funkční kompromis je samostatný stroj pro runner a privátní síť mezi ním a produkcí: sestavení je izolované a provoz deploye se nedostane do veřejného internetu.
7. Závěr
Self-hosted runner pro GitHub Actions se nainstaluje pár příkazy a hlavní práce začíná až po nich. Co si z této části odnést:
- Agent nepotřebuje otevřené porty. Sám jde na GitHub přes port 443, takže klidně žije v uzavřené síti hned vedle produkce.
- Štítky jsou směrovací mechanismus.
runs-onhledá agenta se všemi vyjmenovanými štítky a úloha visící ve frontě téměř vždy znamená neshodu právě tady. - Trvalé prostředí zrychluje i hromadí. Cache mezi sestaveními je výhra, zapomenuté soubory a klíče jsou riziko. Úklid po sobě se stává součástí workflow.
- Environments jsou důležitější než jednotlivé secrets. Environment s revizory a omezením větví dává jak oddělení přístupů, tak ruční krok před produkcí.
- Deploy končí kontrolou. Zelený pipeline bez health-checku znamená jen to, že se provedly příkazy, ne že aplikace funguje.
Minimální funkční sada vypadá takto: samostatný uživatel pro agenta, systemd služba, štítky podle typu úloh, timeout-minutes v každé úloze, secrets v prostředí production a úklid klíčů v kroku s if: always().
Co bude dál v sérii
Ve čtvrté části uděláme totéž pro GitLab CI: nainstalujeme GitLab Runner na VPS, rozebereme executory (shell, docker, docker+machine) a projdeme cestu od prvního .gitlab-ci.yml až po deploy do produkce. Srovnání s GitHub Actions bude průběžné, protože architektonická rozhodnutí se tam liší výrazněji než syntaxe.
📚 Navigace v sérii:
Čtete část 3 ze 6 „GitHub Actions self-hosted runner: instalace a první pipeline na VPS".
Předchozí: ← Část 2. Self-hosted CI/CD: koncept, architektura a runnery
Další: Část 4. GitLab CI self-hosted runner: instalace a první pipeline na VPS →
🚀 VPS a dedikované servery pro vaše GitHub Actions runnery
Runner naráží na CPU během sestavení, na disk během instalace závislostí a na síť během deploye. Hostiserver pro to dává předvídatelné zdroje a síť, ze které lze bezpečně dosáhnout na produkci.
🖥️ Dedikované servery
- Od $90/měsíc, plná kontrola nad hardwarem pro náročná a paralelní sestavení
- NVMe úložiště: rychlá cache npm, Maven a Docker vrstev mezi běhy
- Bez limitu minut sestavení: platíte za server, ne za čas pipeline
- Privátní síť mezi runnerem a produkčními servery
- Podpora 24/7: inženýři pomohou s nastavením agenta a deploye
💻 Cloud (VPS) hosting
- Od $19.95/měsíc, KVM izolace, vyhrazené vCPU a RAM
- Ideální pro prvního runnera: rozjet, připojit k repozitáři, vyzkoušet na reálném projektu
- Snadné škálování: samostatný VPS pro build a samostatný pro deploy, s různými štítky
💬 Nejste si jistí, kterou variantu potřebujete?
💬 Napište nám a se vším pomůžeme!
Časté dotazy
- Lze umístit runner na stejný server, kde běží produkce?
Technicky ano a pro malý projekt to funguje. Problém je v konkurenci o zdroje: sestavení sežere CPU a paměť přesně ve chvíli, kdy se nasazuje release, tedy v momentě největší zátěže aplikace. Druhý problém jsou práva: agent stojící vedle produkce obvykle dostane přístup k souborům aplikace a k restartu služeb, a spolu s ním tento přístup dostane jakýkoli kód, který se v pipeline provede. Kompromis je samostatný levný VPS pro runner a privátní síť do produkce.
- Kolik zdrojů VPS potřebuje jeden runner?
Samotný agent spotřebuje v klidu zhruba 150-250 MB RAM, zbytek závisí na sestaveních. Pro typický webový projekt na Node.js nebo Pythonu stačí 2 vCPU a 4 GB RAM, pro sestavení s Dockerem je lepší 4 vCPU a 8 GB. Disk je důležitější, než se zdá: 40 GB je minimum, protože
_work, cache balíčků a Docker vrstvy rostou rychle. Jeden agent provádí jednu úlohu najednou, takže pro paralelní sestavení buď přidáte další agenty, nebo vezmete výkonnější server.
- Runner ukazuje stav Offline, přestože služba běží. Co zkontrolovat?
Nejprve logy:
journalctl -u 'actions.runner.*' -n 100a adresář_diagve složce agenta. Nejčastější příčiny jsou blokování odchozích spojení na port 443 na github.com ze strany firewallu nebo firemní proxy, zastaralá verze agenta, kterou GitHub už nepřijímá, a docházející místo na disku. Samostatný případ je smazaný runner na straně GitHubu: agent dál běží, ale jeho token už není platný a pomůže jen opětovná konfigurace s novým tokenem.
- Je bezpečné zapnout self-hosted runner ve veřejném repozitáři?
Ne, a GitHub na to přímo upozorňuje. Kdokoli může otevřít pull request a jeho kód se provede na vašem serveru. Prostředí je přitom trvalé, takže důsledky po dokončení úlohy nezmizí: zapomenutý proces, změněný systémový balíček, přečtené soubory z pracovního adresáře. Pro veřejné projekty berte GitHub-hosted runnery. Pokud tam vlastního agenta opravdu potřebujete, zůstává varianta s ephemeral agenty v jednorázových kontejnerech a povinným potvrzením spuštění pro externí přispěvatele.
- Jak spustit více runnerů na jednom serveru?
Každý agent žije ve vlastním adresáři s vlastní konfigurací. Vytvoříte
~/actions-runner-1,~/actions-runner-2, v každém spustíteconfig.shs unikátním--name, paksvc.sh install. Vyjdou z toho dvě nezávislé systemd služby a dvě paralelní úlohy. Rozumná hranice je počet jader dělený dvěma: agenti soupeří o CPU a disk, a čtyři souběžná sestavení na dvoujádrovém VPS budou pomalejší než dvě po sobě.
- Je potřeba actions/cache, když je prostředí stejně trvalé?
Většinou ne. Na trvalém agentovi zůstávají
node_modules, adresář~/.m2nebo~/.cache/pipmezi běhy, takžeactions/cachepřidává zbytečný cyklus nahrání a stažení přes síť GitHubu. Výjimkou je více agentů: sdílená cache pak vyrovná čas sestavení bez ohledu na to, kterému agentovi úloha připadla. Pro ephemeral agenty je cache povinná, protože prostředí je pokaždé nové.
- Co dělat, když na runneru chybí potřebná verze jazyka nebo utility?
Dvě cesty. První jsou akce typu
setup-node,setup-python,setup-java: samy stáhnou potřebnou verzi a cachují ji na agentovi, přičemž verze zůstává popsaná v repozitáři. Druhá je provádět kroky v kontejneru přes klíčcontainerna úrovni úlohy, a pak je prostředí plně popsané image. Ruční instalace balíčků na serveru funguje také, ale vrací stejný problém, kterým jsme sérii začínali: prostředí zná jen ten, kdo ho nastavil.