Naar de hoofdinhoud

CI-runners

fremforge biedt gehoste runners die bij elk abonnement per gebruiker zijn inbegrepen, en ondersteunt eigen runners (BYO) voor teams die on-premises, eigen hardware of een specifieke plek in het netwerk nodig hebben. Beide typen runners voeren workflows van Forgejo Actions uit met dezelfde YAML-syntaxis als GitHub Actions.

Gehoste runners

Gehoste runners zijn virtuele machines per commit op T Cloud Public in de regio eu-de (Biere/Magdeburg). Een VM wordt ingericht vanuit een eigen debian-image met Docker vooraf geïnstalleerd, voert de jobs van één commit van één repository uit en wordt verwijderd zodra de jobs van die commit klaar zijn. Er blijft niets bewaard tussen commits.

Beschikbare labels

LabelOS / architectuurOpmerkingen
fremforgedebian 13 (“trixie”, glibc), x86_64Standaardlabel voor gehoste runners. Gebruik dit voor vrijwel elke workflow. Zie Inhoud van de runner-image voor wat er vooraf is geïnstalleerd.
linux/amd64debian 13 (“trixie”, glibc), x86_64Synoniem voor fremforge. Gebruikt dezelfde image.
dind-privilegeddebian 13, x86_64, KVM-nestingVoeg toe naast fremforge (runs-on: [fremforge, dind-privileged]) voor workflows die KVM-geneste gasten nodig hebben: kind-clusters, functies van testcontainers met capabilities voor het aanmaken van namespaces. Wordt ingepland op een speciaal VM-type met geneste virtualisatie. Dezelfde isolatie van één VM per commit.

Verwijs in je workflow naar een label:

jobs:
  build:
    runs-on: fremforge

Containerbuilds werken direct op twee manieren; zie Inhoud van de runner-image § Containerbuilds hieronder voor de volledige uitsplitsing:

  • Native Docker (standaard): docker build, docker compose en docker pull werken gewoon. De Docker-daemon draait native op de VM (geen Docker-in-Docker-sidecar), dus docker gedraagt zich hetzelfde als op een door GitHub gehoste runner. Geschikt voor 90% van de workflows.
  • kaniko: zonder daemon, snellere koude start, betere laagcache bij externe registries. Gebruik dit als je geen BuildKit-specifieke functies nodig hebt.
jobs:
  build-image:
    runs-on: fremforge
    steps:
      - uses: actions/checkout@v4
      - run: docker build -t registry.example.com/$GITHUB_REPOSITORY:$GITHUB_SHA .
      - run: docker push registry.example.com/$GITHUB_REPOSITORY:$GITHUB_SHA 2>&1 | stdbuf -oL tr '\r' '\n' | grep -vE '^(Waiting|)$'

Gehoste ARM64-runners en extra OS-varianten (Ubuntu, Fedora, …) staan op de roadmap (zie de changelog); fremforge richt zich op dit moment alleen op x86_64 Linux. Eigen runners (zie hieronder) zijn in de tussentijd de route voor ARM64- of niet-Linux-workflows.

Isolatiemodel

Jobs draaien op eigen virtuele machines met een eigen kernel. Het platform houdt de wachtrij van Forgejo Actions in de gaten en start voor een wachtende job een nieuwe VM. Een koude start duurt ongeveer 40 seconden tot de eerste stap van de job; er is geen pool met vooraf gestarte VM’s.

Eén commit, één vertrouwensdomein. Een VM voert alleen jobs uit van de commit en repository waarvoor hij is gestart. Als een job klaar is, wacht de VM tot 60 seconden op een andere job van dezelfde commit, in welke workflow dan ook, en voert die uit zonder opnieuw op te starten (ongeveer 5 seconden tot de start). Daarna schakelt hij uit en wordt hij verwijderd. Geen enkele VM voert jobs uit van twee commits, twee repository’s of twee organisaties, dus beschouw alle jobs van één commit als één vertrouwensdomein. Inloggegevens die het platform kent (Docker- en npm-logins, de gebruikelijke credentialbestanden), worden tussen jobs gewist, maar toolcaches en opgehaalde images blijven op de VM staan. Zie Beveiligingsmodel van de gehoste runners voor het volledige ontwerp.

Jobs dimensioneren voor gehoste runners

Elke nieuwe VM kost ongeveer 40 seconden opstarttijd vóór je eerste stap, en vaak de wachttijd van 60 seconden na je laatste. Een job die 15 seconden werk doet, houdt een VM dus ongeveer twee minuten bezet. Daarom:

  • Geef een job alleen een eigen VM als het werk ongeveer 2 minuten of langer duurt. Kortere checks (lint, een kleine testsuite, een controlescript) zijn goedkoper en sneller als extra stappen in een bestaande job.
  • Verdeel lang, onafhankelijk werk (testshards, imagebuilds, end-to-end-suites) over jobs. Daar is gelijktijdigheid voor.
  • Verdeel kort werk binnen één job in plaats van over jobs, bijvoorbeeld meerdere tofu plan-runs als achtergrondprocessen in één stap. Die wachten vooral op API’s, niet op de CPU.
  • Ketens hergebruiken VM’s automatisch. Een needs:-job die klaar is om te starten terwijl de VM van zijn voorganger nog wacht (binnen 60 seconden), draait zonder opstarten op die VM. Een brede uitwaaiering van jobs die allemaal tegelijk starten, kan niets hergebruiken: elke job heeft een eigen VM nodig.
  • Houd een run binnen je gelijktijdigheid. Een matrix die breder is dan het gegarandeerde aandeel van je organisatie, draait in golven, en de laatste golf wacht tot een hele job klaar is. Dimensioneer shards zo dat een run in één golf past.

Geheugen

Een VM van een gehoste runner heeft 4 GiB geheugen en geen swap (2 vCPU). Er is op dit moment geen grotere gehoste runner.

Een job die zonder geheugen komt te zitten, faalt binnen ongeveer een minuut. Zakt het beschikbare geheugen onder ongeveer 4% (zo’n 130 MiB), dan stopt de runner het grootste proces van de job, bij voorkeur buildtools zoals node, tsc, java en vitest-workers. Hij stuurt eerst SIGTERM, en SIGKILL als het geheugen blijft dalen. De stap faalt met exitcode 143 of 137, en het log van de stap zegt waarom:

earlyoom: killed node (pid 4242) because the runner VM ran out of memory. ...

De runner zelf is nooit het proces dat wordt gestopt, dus het resultaat en de logs van de job worden nog steeds gemeld. Vóór deze wijziging kon een job die te groot werd voor de VM ongemerkt blijven hangen tot zijn timeout-minutes.

Zie je deze regel, verlaag dan het piekgeheugen van de job:

  • Verdeel het werk over jobs. Een grote build naast zijn integratietests draaien, of een docker build naast testcontainers, telt de twee pieken op één VM bij elkaar op.
  • Begrens het geheugen van de tool. Bijvoorbeeld NODE_OPTIONS=--max-old-space-size=2048 voor Node, --checkers 1 en GOMEMLIMIT voor TypeScript 7 (tsc geschreven in Go), -Xmx voor Java, minder testworkers (--maxWorkers / --pool-options).
  • Stream in plaats van in te laden. Zie voor grote imagescans Grote images scannen met Trivy.

Past een job niet in 4 GiB, neem dan contact op met support@frem.sh, of draai hem op een eigen runner die er groot genoeg voor is.

Inbegrepen minuten en gelijktijdigheid

OnderdeelWaarde
Inbegrepen minuten1.000 runnerminuten per gebruiker per maand, gedeeld binnen de organisatie
Gelijktijdigheid2 gelijktijdige jobs per gebruiker gegarandeerd (minimaal 10, maximaal 60); tot 60 zolang het platform capaciteit over heeft. Proefperiode: 2.
Maximale looptijd per jobstandaard 60 min (via --timeout); een absoluut maximum van 6 uur wordt op de VM afgedwongen. timeout-minutes: op jobniveau in je workflow-YAML begrenst kortere waarden; timeout-minutes: op stapniveau wordt al gerespecteerd.
Kosten per minuut binnen de poolGeen, inbegrepen in het vaste abonnement van € 30 per gebruiker
Als de pool op isNieuwe jobs worden geweigerd met een duidelijke commitstatuscheck fremforge/runner-minutes-cap; lopende jobs maken het gewoon af. Facturatie van overschrijding is optioneel, zie hieronder.
Minuten worden geresetOp de 1e van elke maand; ongebruikte minuten worden niet meegenomen

Jobs boven de limiet van je organisatie wachten in de wachtrij in plaats van te falen. Neem contact op met support@frem.sh als je een hoger gegarandeerd aandeel nodig hebt.

Facturatie van overschrijding (optioneel): standaard komen nieuwe jobs in de wachtrij als de pool op is. Eigenaren van de organisatie kunnen gemeten overschrijding inschakelen via Organisatiebeheer → Facturatie → Verbruik van runner-minuten. Staat dat aan, dan worden minuten boven de pool op de volgende factuur in rekening gebracht tegen € 0,01 per minuut. Overschrijding kan op dezelfde pagina op elk moment weer worden uitgeschakeld.

Verbruiksdashboard

Volg het verbruik van runnerminuten via Organisatiebeheer → Facturatie → Verbruik van runner-minuten.

Het verbruiksdashboard toont:

WeergaveWat het toont
MaandoverzichtTotaal aantal gebruikte minuten in deze factuurperiode tegenover de inbegrepen pool (1.000 min × aantal gebruikers)
Uitsplitsing per repositoryMinuten die elke repository heeft verbruikt, te sorteren op verbruik
Uitsplitsing per workflowMinuten die elk workflowbestand binnen een repository heeft verbruikt
TijdlijnGrafiek van het dagelijkse verbruik voor de huidige en de vorige factuurperiode

Minuten worden geteld van de start tot het einde van de job, naar boven afgerond op de seconde. Wachttijd in de wachtrij (wachten op een runner) telt niet mee.

De maandperiode wordt op de 1e van elke maand (UTC) gereset. Ongebruikte minuten worden niet meegenomen.

Minuten van eigen runners worden apart bijgehouden in hetzelfde dashboard en tellen niet mee voor de gehoste pool.

Drempels voor verbruiksmeldingen die je zelf kunt instellen, zijn nog niet beschikbaar: fremforge stuurt standaard een waarschuwingsmail wanneer de organisatie 80% van de maandelijkse pool bereikt. Neem contact op met support@frem.sh om de drempel aan te passen.

Inhoud van de runner-image

De gehoste runner-image wordt wekelijks (ma 06:00 UTC) opnieuw gebouwd vanuit de upstream-versies van tools die Renovate volgt. Elke download tijdens de build is vastgezet met sha256 (de build faalt bij afwijkingen). Tijdens de build draait een CVE-poort van Trivy: een image met HIGH- of CRITICAL-bevindingen wordt tegengehouden totdat hij is gepatcht.

Voordat een opnieuw gebouwde image live gaat op de runnervloot, wordt hij nogmaals gecontroleerd tegen de lijsten met actief uitgebuite kwetsbaarheden (ENISA EUVD en CISA KEV). Een image met een actief uitgebuite CVE waarvoor een oplossing beschikbaar is, wordt ongeacht de ernst niet uitgerold, tenzij een beoordeelde uitzondering van maximaal 30 dagen die CVE dekt. Runners blijven de vorige image gebruiken totdat er een gepatchte is gebouwd.

Model met twee lagen, beide in de EU:

  • Systeemlaag (~300 MB): debian-basis + systeemtools in één versie (Node 24 LTS, build essentials, scanners, IaC- en cloud-CLI’s, browsers, databaseclients). Onderdeel van de opstartschijf van de VM; geen download per job.
  • hostedtoolcache-laag (~7 GB): taaltoolchains in meerdere versies (Node, Go, Python, .NET, Java, Ruby) + Android SDK + Miniconda3 + R (sinds 2026-06-06). Ingebakken in dezelfde opstartschijf van de VM op /opt/hostedtoolcache. setup-*-actions halen hun versie hier vandaan, zonder download per job.

Hoe we volledige dekking leveren in ~7 GB in totaal, terwijl ubuntu-latest van GitHub ~50 GB is: zie het verhaal over de grootte hieronder.

Vooraf geïnstalleerd in de systeemlaag:

CategorieWat
Basisdebian 13 (“trixie”) slim (glibc; vereist voor ubuntu-compatibele binaries uit setup-*-actions)
Shell + GNU coreutilsbash, coreutils, util-linux, findutils, grep, sed, gawk, diffutils, tzdata, locale
Netwerk + cryptografieca-certificates, curl, wget, openssl, openssh-client, gnupg
Versiebeheergit, git-lfs (voor het hele systeem geïnitialiseerd), tea (Forgejo-CLI)
Archieventar, gzip, xz, bzip2, unzip, zip, zstd
Build essentialsbuild-essential (gcc + make), clang, lld, ninja, cmake, pkg-config, autoconf, automake, libtool
JS-runtime (systeem)Node 24 LTS + npm + pnpm en yarn beheerd door corepack
Andere talen (systeem)Rust (rustup stable), PowerShell 7, PHP 8.3 + Composer
JVM-buildMaven 3.9, Gradle 9.8 (de Java-SDK’s komen uit de hostedtoolcache)
IaCOpenTofu, Bicep, Pulumi, Ansible (via pipx), AWS SAM CLI
Cloud-CLI’sAWS CLI v2, Azure CLI, Google Cloud CLI (gcloud + gsutil + bq)
ContainersDocker-CLI + native Docker-daemon (geen DinD-sidecar; zie Containerbuilds), kaniko, skopeo, kind
K8skubectl, helm, kustomize
Beveiliging + lintgitleaks, trivy, osv-scanner, cosign, syft, opengrep, scorecard, checkov, cfn-lint, actionlint, hadolint, shellcheck, shfmt, yamllint
Gegevensvormenjq, yq
Databaseclientspostgresql-client, default-mysql-client, sqlite3
Browsers + e2eChromium + chromedriver, Firefox ESR + geckodriver, Selenium Server
Inittini als PID 1 (nette afhandeling van signalen + opruimen van zombieprocessen)
Executorforgejo-runner (upstream act_runner, vastgezet) voert de stappen van Forgejo Actions native op de VM uit

Vooraf geïnstalleerd in de hostedtoolcache (opgehaald door setup-*-actions, zonder download per job):

ToolVersies / inhoud
Node.js26.x, 24.x, 22.x (LTS)
Go1.27.x, 1.26.x
Python3.14, 3.13, 3.12
.NET SDK10.0, 8.0
Java (Temurin)25, 21, 17
Ruby4.0.x, 3.4.x, 3.3.x
Android SDKcmdline-tools + platform 34 + build-tools 34.0.0 + platform-tools; ANDROID_HOME=/usr/local/lib/android/sdk; extra platforms/NDK per job via setup-android@v3
Miniconda3conda + python 3.11; CONDA=/opt/hostedtoolcache/Miniconda3/latest/x64; omgevingen per job aangemaakt via setup-miniconda@v3
R (CRAN)r-base + r-base-dev 4.3.x; opgehaald door r-lib/actions/setup-r@v2; CRAN-pakketten per job geïnstalleerd

De officiële versielijst met de exacte versies + image-digest + samenvatting van de Trivy-scan + CycloneDX-SBOM wordt bij elke wekelijkse herbouw gepubliceerd; zie de referentie van de runner-image voor de actuele toolstack.

Containerbuilds: native Docker of kaniko

Twee routes; kies op basis van de workload:

Native Docker (standaard): de VM wordt geleverd met de Docker-daemon geïnstalleerd en draaiend op de host. Je docker-CLI praat ermee via de standaard lokale socket: geen omweg via DOCKER_HOST, geen sidecar. Werkt voor docker build, docker compose, docker pull, multi-stage builds en BuildKit-functies. Een directe vervanging voor docker op de door GitHub gehoste runners.

- run: docker build -t myapp:${{ github.sha }} .
- run: docker push myapp:${{ github.sha }} 2>&1 | stdbuf -oL tr '\r' '\n' | grep -vE '^(Waiting|)$'

Grote images scannen met Trivy

trivy image <ref> (via de daemon) houdt de laagmetadata van de hele image in het geheugen terwijl het het bestandssysteem doorloopt; images van meerdere GB kunnen ruim boven de geheugenlimiet van de runner-VM uitpieken en midden in de scan een OOM-kill veroorzaken. Het betrouwbare patroon voor elke imagegrootte is de image te streamen via docker save:

- run: docker build -t myapp:${{ github.sha }} .
- run: |
    docker save myapp:${{ github.sha }} | trivy image \
      --severity HIGH,CRITICAL --ignore-unfixed \
      --scanners vuln --pkg-types os \
      --offline-scan --no-progress --quiet \
      --input -

Waarom het werkt: docker save leest de lagen van schijf en streamt de tarball laag voor laag naar de stdin van trivy. Trivy leest uit elke laag de metadata van de OS-pakketten (~tientallen KB per laag) en gooit die weg vóór de volgende laag. Het gezamenlijke piek-RSS blijft rond de 500 MB, ongeacht de totale grootte van de image, dus de scan past ruim binnen het geheugenbudget van de runner-VM. GOGC=10 vóór trivy (bijv. GOGC=10 trivy ...) maakt de heap nog krapper als je ooit dicht bij de limiet komt.

Het filter tr '\r' '\n' op docker push wordt aanbevolen: Docker schrijft de voortgang van het uploaden van lagen met carriage returns die de regel overschrijven, en het staplog van Forgejo Actions behandelt dat als één steeds groeiende regel die ongemerkt wordt afgekapt. Zonder het filter toont een push die halverwege blijft hangen of een fout geeft, in het log alleen <layer>: Preparing zonder details over de fout; tegen de tijd dat de stap een time-out krijgt, is de eigenlijke fout overschreven. Door via tr te pipen wordt elke voortgangsstap een eigen regel die volledig wordt vastgelegd, en grep -vE '^(Waiting|)$' haalt lege en inactieve opvulregels weg. De doorvoer van het pushen van lagen verandert niet.

Registries van klanten (ghcr.io, dockerhub, quay.io, je eigen) zijn bereikbaar via de egress van de runner; de proxy heeft een time-out van 30 minuten per verzoek, zodat PUT’s van lagen van meerdere GB netjes afronden. Budget voor de imagegrootte: 50 GB per build (de limiet van de scratch-schijf van de VM); dien een ticket in als je meer nodig hebt.

kaniko: OCI-bouwer zonder daemon en zonder root. Snellere koude start dan de volledige Docker-daemon, en gaat beter om met de laagcache bij externe registries. Gebruik dit als de build geen BuildKit-specifieke functies nodig heeft.

- run: |
    /usr/local/bin/kaniko \
      --context=$GITHUB_WORKSPACE \
      --dockerfile=Dockerfile \
      --destination=registry.example.com/$GITHUB_REPOSITORY:$GITHUB_SHA

Voeg voor kind- / testcontainers-workloads die KVM-geneste gasten nodig hebben, het label dind-privileged toe aan runs-on. fremforge stuurt deze naar een VM-type met geneste virtualisatie.

De grootte vergeleken met GitHub

ubuntu-latest van GitHub is ~50 GB omdat het het volgende inbakt:

  • Meerdere versies per taal: 3 recente reeksen voor Node, Python, Java en Ruby; 2 voor Go en .NET (~3 GB in totaal). De tabel hierboven is de gezaghebbende set.
  • Android SDK + NDK (~10 GB bij GitHub): wij bakken de kern-SDK in (platform 34 + build-tools + platform-tools, ~3 GB); extra API-niveaus + NDK + emulators installeer je per job via setup-android@v3 (geen nieuwe download van wat al is ingebakken).
  • Vooraf opgehaalde Docker-images voor servicecontainers (postgres, mysql, mongo, redis, mssql ~5 GB): niet ingebakken; een docker pull haalt ze op van Docker Hub, of via de dependency-proxy van je organisatie (zie pakketcaches).
  • De browser Microsoft Edge: niet ingebakken (Chromium is voor headless tests in CI dezelfde engine).
  • Mono / Julia / Swift / Haskell / Lua / Crystal / Nim / Elixir: niet ingebakken (niche, per job te installeren via setup-* of apt).
  • Meerdere GCC-versies, oude toolchains die voor compatibiliteit worden bewaard: wij leveren één actuele set.

We missen geen tools die de gewone workflow gebruikt: elke gangbare taal, elke grote cloud-CLI, elke gangbare scanner, beide grote browsers, Docker (native, root via het label dind-privileged), Android (kern-SDK) en datawetenschap (Miniconda3 + R). iOS / Xcode werkt niet op Linux en wordt apart gevolgd; nichetalen (Julia/Swift/Haskell/Mono) installeer je per job. Laat het ons weten als je team iets nodig heeft wat we niet inbakken, dan bekijken we het voor de volgende wekelijkse herbouw van de image.

Zie voor alle details per tool docs.frem.sh/build/actions/runner-image/.

Eigen runners (BYO)

Registreer je eigen runners bij elke fremforge-organisatie (on-premises servers, cloud-VM’s, Mac minis, Raspberry Pi’s). Minuten van eigen runners tellen niet mee voor de gehoste pool.

Registratie

  1. Ga in de Forgejo-instellingen van de organisatie naar Instellingen → Actions → Runners → Nieuwe runner aanmaken (frem.sh/org/<org>/settings/actions/runners).
  2. Kopieer het registratietoken.
  3. Download het binary van de Forgejo-runner. De actuele stabiele release wordt gepubliceerd op code.forgejo.org/forgejo/runner/releases; zet bij de installatie een recente tag vast:
# Linux x86_64 — replace <version> with the tag from the releases page (e.g. v6.4.0)
curl -sSL https://code.forgejo.org/forgejo/runner/releases/download/<version>/forgejo-runner-<version>-linux-amd64 \
  -o forgejo-runner && chmod +x forgejo-runner

# macOS arm64
curl -sSL https://code.forgejo.org/forgejo/runner/releases/download/<version>/forgejo-runner-<version>-darwin-arm64 \
  -o forgejo-runner && chmod +x forgejo-runner

fremforge volgt de actuele releases van Forgejo; runners met oudere binaries blijven werken, maar horen binnen één releasecyclus te worden bijgewerkt om CVE-patches mee te krijgen.

  1. Registreer de runner:
./forgejo-runner register \
  --instance https://frem.sh \
  --token <registration-token> \
  --name my-runner \
  --labels linux,self-hosted,my-custom-label
  1. Start de runner:
./forgejo-runner daemon

Ondersteunde platforms

PlatformArchitecturen
Linuxx86_64, ARM64, ARMv7
macOSx86_64, Apple Silicon (ARM64)
Windowsx86_64

Eigen labels

De labels die je bij de registratie toekent, zijn waar je in runs-on: naar verwijst:

jobs:
  deploy:
    runs-on: [self-hosted, my-custom-label]

Meerdere labels in een lijst betekenen “zoek een runner die AL deze labels heeft”. Het label self-hosted is een conventie. Neem het op om te voorkomen dat een job voor een eigen runner per ongeluk naar een gehoste runner gaat.

Gebruik geen gereserveerde labels. De labels fremforge en linux/amd64 gaan naar onze pool met gehoste runners (de oudere aliassen fremforge-cci en cci zijn verouderd en gaan dezelfde kant op). Kies voor je eigen runners afwijkende labels (bijv. self-hosted, on-prem, my-team-runner), zodat workflows ze ondubbelzinnig kunnen aanspreken.

Netwerkeisen voor de runnerhost

Het runnerbinary opent long-poll-HTTPS-verbindingen naar https://frem.sh en kloont repository’s via HTTPS. Vanuit de host gezien zijn dit de eisen aan de verbinding:

BestemmingPoortDoel
frem.sh443Runnerprotocol via Connect-RPC (/api/actions/runner.v1.RunnerService/*), runtime-API’s van Actions, git clone via HTTPS
*.frem.sh443Gereserveerd voor subdomeinen van organisaties, downloads van bijlagen en ruwe bestanden
Action-mirror (indirect)443Gebruikt een workflow uses: actions/<name>@<ref>, dan haalt de runner de action op van frem.sh/<owner>/<repo>; dezelfde host als hierboven
Doelen van workflows van de klantwisselendWelke bestemmingen je workflows ook aanspreken (deploydoelen, pakketregistries, enz.)

Er is geen inkomende verbinding nodig: de runner zet alle verbindingen zelf naar buiten op. Een runner achter NAT of een firewall werkt prima, zolang hij frem.sh:443 kan bereiken.

Verantwoordelijkheid voor beveiliging en isolatie

Bij gehoste runners zorgt fremforge voor isolatie op kernelniveau tussen commits en tussen klanten (elke commit draait in een nieuwe, tijdelijke ECS-VM; jobs van dezelfde commit kunnen die delen). Bij eigen runners ben je zelf verantwoordelijk voor de isolatie. Een zelf gehoste runner die workflows uit meerdere repository’s afhandelt, deelt toestand tussen jobs, tenzij je elke runner beperkt tot één repository of organisatie.

Draai je workflows van meerdere teams of vertrouwensdomeinen, behandel je eigen runner dan zoals zelf gehoste runners van GitHub Actions worden behandeld:

  • Eén runner per repository (of per vertrouwensgrens) als secrets gevoelig zijn.
  • Registreer geen eigen runner bij een openbare repository: iedereen met merge-rechten zou een workflowwijziging kunnen doorvoeren die willekeurige code op je runnerhost uitvoert.
  • Behandel de runnerhost als productie: verhard het besturingssysteem, houd het runnerbinary gepatcht en leid het uitgaande verkeer door je eigen SSRF- / egresscontroles.

De SSRF-proxy voor uitgaand verkeer van fremforge geldt NIET voor eigen runners, alleen voor gehoste runners. Het netwerkbeleid van je runner is wat je zelf op de host inricht.

Egress en netwerk

RegelDetail
Uitgaand HTTP/HTTPSToegestaan (poorten 80 en 443)
Uitgaand, andere poortenStandaard geblokkeerd
Inkomende verbindingenGeblokkeerd; runners accepteren geen inkomende verbindingen
Metadata-endpoint van T Cloud Public (169.254.x.x)Geblokkeerd door de SSRF-proxy voor uitgaand verkeer
Private bereiken volgens RFC-1918Geblokkeerd door de SSRF-proxy voor uitgaand verkeer

Egress-allowlist op organisatieniveau: neem contact op met support@frem.sh om runnerjobs uitgaande toegang te geven tot extra CIDR’s of poorten. Egressregels worden op platformniveau ingesteld.

De SSRF-proxy is een ondergrens van het platform. Hij kan niet worden uitgeschakeld. Hij voorkomt dat runnerjobs de instance-metadata van T Cloud Public of interne infrastructuur bereiken.

Secrets in jobs

Secrets worden als omgevingsvariabelen geïnjecteerd en in joblogs gemaskeerd. Elke logregel met de waarde van een secret wordt vervangen door ***.

Syntaxis om ernaar te verwijzen:

steps:
  - name: Deploy
    env:
      API_KEY: ${{ secrets.MY_API_KEY }}
    run: ./deploy.sh

Bereiken van secrets

BereikWaar in te stellenBeschikbaar voor
Secrets van de organisatieOrganisatiebeheer → SecretsAlle repository’s in de organisatie
Secrets van een repositoryRepository → Instellingen → Actions → GeheimenAlleen die repository
Secrets van een omgevingOrganisatiebeheer → Omgevingen → <env> → Omgevings-secretsJobs die naar die benoemde omgeving verwijzen

Voor secrets van een omgeving moet de job een omgeving declareren:

jobs:
  deploy:
    runs-on: fremforge
    environment: production
    steps:
      - run: echo ${{ secrets.PROD_API_KEY }}

Gebruik voor inloggegevens van cloudproviders bij voorkeur OIDC-tokenfederatie in plaats van langlevende secrets. Geen secret om op te slaan, geen secret om te roteren.

Problemen oplossen

Een stap faalt met earlyoom: killed <process> ... ran out of memory.

De job gebruikte meer dan de 4 GiB geheugen van de runner-VM, dus de runner stopte het grootste proces in plaats van de VM te laten vastlopen. Zie Geheugen voor hoe je de piek van de job verlaagt.

Een job blijft in de wachtrij hangen.

De meest voorkomende oorzaak is dat de limiet voor gelijktijdigheid per organisatie is bereikt (2 gelijktijdige jobs per gebruiker gegarandeerd, minimaal 10 en maximaal 60; zie Inbegrepen minuten en gelijktijdigheid). Bekijk Organisatiebeheer → Facturatie → Verbruik van runner-minuten voor het huidige aantal actieve jobs. Zit je aan de limiet, wacht dan tot een lopende job klaar is of neem contact op met support@frem.sh voor een verhoging via Enterprise-on-Demand.

Het docker-commando werkt, maar een handeling die extra rechten vereist faalt (bijv. een kind-cluster aanmaken met KVM-nesting).

De standaard-VM voor runs-on: fremforge draait Docker native met de standaardinstellingen voor een gebruiker zonder root: docker build / docker compose / docker pull werken direct, maar handelingen die extra capabilities nodig hebben (KVM-nesting, rauwe toegang tot /dev voor een geneste gast, bepaalde netwerkmodi van kind-clusters), niet. Voeg het label dind-privileged toe aan runs-on (runs-on: [fremforge, dind-privileged]): fremforge stuurt de job dan naar een VM-type met geneste virtualisatie. Dezelfde isolatie van één VM per commit; dezelfde image; alleen met verhoogde capabilities.

Voor het puur kopiëren of opnieuw taggen van images tussen registries (zonder build) werkt de vooraf geïnstalleerde skopeo zonder dat de volledige Docker-daemon nodig is.

Een secret wordt in de logs gemaskeerd, maar de stap gedraagt zich alsof het leeg is.

Het secret bestaat in de runtime (de maskering bevestigt dat), maar de waarde op het juiste bereik is niet ingesteld. Controleer:

  1. Is het secret ingesteld op het niveau van de organisatie, de repository of de omgeving?
  2. Is het een secret van een omgeving: declareert de job dan environment: <name> met de omgeving van het secret?
  3. Is de naam van het secret precies goed gespeld (hoofdlettergevoelig)?

De secretsinterface op het betreffende bereik toont de naam van het secret en het tijdstip van de laatste wijziging. Gebruik die om te bevestigen dat het secret bestaat voordat je naar de workflow kijkt.

Zie ook