Les runners GitHub Actions de FFxF sont ouverts à tous les comptes, en bêta. Sur le papier, l'argument tient en une ligne : 0,004 CAD la minute en 2 vCPU, soit 0,003 USD, contre 0,006 USD pour le runner Linux de GitHub. Il restait à voir ce que cela donne sur un vrai workflow, en temps comme en argent.
Le 22 octobre 2026, nous avons lancé le même fichier de workflow sur
ubuntu-latest chez GitHub et sur ffxf-2vcpu-ubuntu-2404
chez nous, deux machines de 2 vCPU, deux fois de suite. Voici les chiffres, y compris
là où nous sommes plus lents.
1. Le workflow d'essai
Deux jobs indépendants, choisis parce qu'ils ressemblent à une CI ordinaire :
- go compile GitHub CLI (
cli/cli, version 2.63.0) et lance les tests des commandespretissue, avec le cache deactions/setup-go. C'est surtout du calcul. - docker construit l'image de l'application d'exemple de Docker
(
docker/getting-started-app) surnode:22-slim, avecnpm ci. C'est surtout du téléchargement.
D'un essai à l'autre, seule change l'étiquette passée à runs-on :
on:
workflow_dispatch:
inputs:
label:
default: ffxf-2vcpu-ubuntu-2404
jobs:
go:
runs-on: ${{ inputs.label }}
steps:
- uses: actions/checkout@v4
with:
repository: cli/cli
ref: v2.63.0
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- run: go build -o bin/gh ./cmd/gh
- run: go test ./pkg/cmd/pr/... ./pkg/cmd/issue/...
docker:
runs-on: ${{ inputs.label }}
steps:
- run: |
git clone https://github.com/docker/getting-started-app.git app
cd app
# Dockerfile : FROM node:22-slim, npm ci --omit=dev, COPY . .
docker build -t todo-app .
Les machines, telles que le job les décrit : un AMD EPYC (7763 au premier passage,
9V74 au second) chez GitHub, un Intel Xeon Gold 6252 chez nous, 2 vCPU des deux
côtés. Un détail compte pour la comparaison : ubuntu-latest désigne
maintenant Ubuntu 26.04 chez GitHub, alors que nos runners suivent l'image
ubuntu-24.04.
2. Premier passage, sans cache
| Mesure | GitHub | FFxF |
|---|---|---|
| Attente avant le job docker | 2 s | 7 s |
| Attente avant le job go | 2 s | 30 s |
| Compilation de GitHub CLI | 97 s | 88 s |
| Tests | 24 s | 21 s |
| Job go complet | 152 s | 145 s |
docker build | 12 s | 39 s |
| Job docker complet | 14 s | 49 s |
| Minutes facturées | 4 | 4 |
| Prix du passage | 0,024 USD | 0,016 CAD (0,012 USD) |
Sur le calcul, notre Xeon compile GitHub CLI 9 secondes plus vite et passe les tests 3 secondes plus tôt. Les deux services arrondissent chaque job à la minute supérieure : 4 minutes de part et d'autre, et une facture deux fois plus petite chez nous.
L'attente du job go vient du démarrage. Nous gardons des runners prêts selon le nombre de jobs que le compte lance en même temps ; ce dépôt n'en lançait qu'un à la fois jusque-là. Le job docker a pris le runner prêt, le job go a attendu qu'une machine neuve démarre : 30 secondes. Ces secondes ne sont pas facturées, la minute ne court qu'une fois le job commencé.
Le docker build, lui, est trois fois plus lent chez nous. Le temps part
dans deux téléchargements : l'image node:22-slim (9,9 s contre 2,7 s)
et les paquets de npm ci (9,8 s contre 3,7 s). Nos runners sortent en
IPv6 ; Docker Hub n'a que l'IPv4, et les conteneurs d'un build sortent eux aussi en
IPv4, si bien que ces téléchargements passent par une traduction d'adresses. Chez
GitHub, ils restent dans un réseau voisin des registres. C'est aujourd'hui notre
point faible, et la raison d'être du cache Docker, plus bas.
L'étape Set up runner (6 à 8 s dans nos jobs) branche le cache Docker du dépôt ; elle compte dans les minutes du job.
3. Deuxième passage, caches chauds
| Mesure | GitHub | FFxF |
|---|---|---|
| Restauration du cache Go | 25 s | 23 s |
| Compilation | 3 s | 3 s |
| Tests | 6 s | 6 s |
| Job go complet | 46 s | 50 s |
docker build | 19 s | 43 s |
| Job docker complet | 24 s | 54 s |
| Minutes facturées | 2 | 2 |
| Prix du passage | 0,012 USD | 0,008 CAD (0,006 USD) |
Le cache de setup-go (461 Mo) se restaure aussi vite des deux côtés.
Chez nous, il est gardé à Montréal, à côté des runners, sans rien changer au
workflow, et facturé 0,30 CAD par Go et par mois. Avec ce cache, la compilation
tombe à 3 secondes partout.
Côté Docker, GitHub repart d'une machine vide à chaque job, donc sans couche en cache : 19 secondes. Chez nous, le cache Docker aurait dû servir, et il n'a pas servi. La règle de l'époque l'explique : le cache Docker d'un dépôt était celui du dernier job réussi. Ici, le job go a fini après le job docker, et c'est son état qui a été gardé, alors qu'il n'avait pas touché à Docker. C'est corrigé depuis le 22 octobre.
4. Le cache Docker, quand il sert
Depuis le correctif, seul un job réussi qui a changé Docker met le cache à jour. Le runner relève l'état de Docker au début du job (images, cache de BuildKit, volumes nommés) et le compare à la fin. Le même workflow à deux jobs, relancé une seconde fois, donne ceci :
FFxF: Docker cache of this repository attached (1.9 GB, saved 119 s ago), replaced only if this job changes Docker and succeeds.
#6 CACHED
#7 CACHED
#8 CACHED
docker build : 4 s (code 0)
FFxF: Docker changed by this job (images or build cache): if the job succeeded, its Docker state becomes the cache of this repository.
FFxF: Docker unchanged by this job: the Docker cache of this repository stays as it is.
Quatre secondes, contre 39 à 43 secondes sans cache chez nous et 12 à 19 secondes
chez GitHub. Le job entier dure 14 secondes, dont 8 pour brancher le cache. L'image
node:22-slim n'est plus téléchargée, et npm ci ne
repasse que quand package-lock.json change. Le job go n'a pas touché à
Docker : il laisse le cache tel quel, même s'il finit après.
Pour en profiter :
- activez le cache Docker sur les dépôts qui construisent des images (console, Runners GitHub, carte Cache Docker), sans rien changer à vos workflows ;
- la mise à jour se fait à la fin du job, en une dizaine de secondes. Un job qui
démarre pendant ce temps, comme celui d'un
needs:, l'attend (30 secondes au plus) ; - deux jobs qui changent Docker en même temps partent du même cache, et le dernier qui réussit l'emporte.
5. Quand c'est intéressant
À minutes égales, la facture est divisée par deux en 2 et 4 vCPU, et un peu plus en 8 vCPU (0,012 USD la minute contre 0,022 USD chez GitHub). Les forfaits de GitHub incluent un quota de minutes chaque mois ; les jobs qui tournent sur nos runners ne l'entament pas, et le gain commence dès que vos workflows le dépassent ou demandent plus de 2 vCPU.
Les jobs de calcul (compilation, tests) vont au moins aussi vite que chez GitHub. Les jobs qui téléchargent beaucoup depuis Docker Hub à chaque passage sont plus lents chez nous tant que le cache Docker ne les couvre pas.
6. Essayer
- Dans la console, ouvrez Runners GitHub et cliquez sur Connecter GitHub : installez l'app FFxF Runners sur une organisation ou sur votre compte, et choisissez ses dépôts.
- Remplacez
ubuntu-latestparffxf-2vcpu-ubuntu-2404(ou4vcpu,8vcpu) dans un workflow. - Les minutes de chaque dépôt s'affichent dans la console et dans l'API. Chaque mois, 500 minutes de 2 vCPU sont offertes dès que le compte a fait un paiement.
Pendant la bêta, seuls les dépôts privés de GitHub.com sont servis, sous Linux x64. Les tailles, les prix et les limites sont sur la page des runners GitHub, et le détail technique dans le guide.