GitHub Actions : lancer ses workflows sur son propre serveur
Les runners GitHub-hosted ont des limites. Voici comment installer un self-hosted runner sur un VPS et reprendre le contrôle sur l'exécution de vos pipelines.
Un runner, c'est la machine qui exécute vos jobs GitHub Actions. Quand vous écrivez runs-on: ubuntu-latest, GitHub démarre une VM fraîche quelque part dans son infrastructure, exécute votre workflow, puis la détruit. Pratique. Mais cette VM ne connaît pas votre réseau privé, n'a pas accès à votre base de données interne, et si votre projet dépasse 2 000 minutes mensuelles sur un compte gratuit — vous payez.
Un self-hosted runner change l'équation. Vous installez un agent sur votre propre serveur. GitHub Actions le détecte, lui envoie les jobs, et vos workflows tournent sur votre infrastructure. Même pipeline YAML, même expérience dans l'interface GitHub, mais exécution locale.
Quand ça vaut vraiment le coup
Pas besoin de self-hosted runner pour un projet perso standard. Les runners GitHub-hosted font très bien leur travail. En revanche, dans ces cas :
Accès au réseau privé. Si vos tests doivent toucher une base PostgreSQL locale, un service interne, ou un registry Docker privé non exposé sur Internet — le runner GitHub-hosted ne peut pas y accéder. Un self-hosted runner sur le même réseau peut.
Dépassement des minutes gratuites. GitHub offre 2 000 minutes/mois sur compte gratuit, 3 000 sur Pro. Une suite de tests lourde sur plusieurs branches peut les consommer vite. Sur votre VPS, pas de compteur.
Architecture spécifique. Besoin de builder pour ARM64, ou de tester sur une config précise ? Les runners GitHub-hosted sont x86_64 Ubuntu/Windows/macOS. Avec un self-hosted, vous utilisez ce que vous voulez.
Vitesse. Les artefacts restent sur la machine entre les runs si vous les gérez bien. Pas de download de dépendances npm depuis zéro à chaque fois — le cache persiste.
Installation sur un VPS Linux
Je pars d'un serveur Ubuntu 22.04. Assurez-vous d'avoir configuré votre accès SSH et ouvert les ports nécessaires avec UFW avant de commencer.
Créez un utilisateur dédié — ne faites jamais tourner un runner en root.
sudo useradd -m -s /bin/bash github-runner
sudo usermod -aG docker github-runner # si vous utilisez Docker dans les workflows
sudo su - github-runnerDans GitHub, allez dans votre repo → Settings → Actions → Runners → New self-hosted runner. GitHub génère une commande d'installation avec un token unique valable 1 heure. Elle ressemble à ça :
mkdir actions-runner && cd actions-runner
# Télécharger le runner (vérifiez la dernière version sur la page GitHub)
curl -o actions-runner-linux-x64-2.317.0.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-linux-x64-2.317.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.317.0.tar.gzConfiguration avec le token fourni par GitHub :
./config.sh \
--url https://github.com/votre-org/votre-repo \
--token VOTRE_TOKEN_ICI \
--name mon-vps-runner \
--labels linux,x64,production \
--work _work \
--unattendedLes --labels sont importants. Ils vous permettent de cibler ce runner précis dans vos workflows avec runs-on: [self-hosted, production].
Lancer en tant que service systemd
Lancer le runner manuellement ne tient pas au reboot. Installez-le comme service :
# Toujours en tant qu'utilisateur github-runner
sudo ./svc.sh install github-runner
sudo ./svc.sh start
sudo ./svc.sh statusLe service s'appelle actions.runner.[org]-[repo].[nom-du-runner]. Vérification :
sudo systemctl status actions.runner.*● actions.runner.mon-org-mon-repo.mon-vps-runner.service
Loaded: loaded (/etc/systemd/system/...)
Active: active (running) since ...À partir de là, le runner redémarre automatiquement au boot et se reconnecte à GitHub.
Utiliser le runner dans un workflow
Un seul changement dans votre YAML : runs-on.
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: [self-hosted, linux, production]
steps:
- uses: actions/checkout@v4
- name: Build
run: npm ci && npm run build
- name: Restart app
run: pm2 restart app[self-hosted, linux, production] matche le runner qui a ces trois labels. Si vous avez plusieurs runners avec des labels différents (staging, production), vous routez les jobs vers le bon serveur.
Vous pouvez aussi mélanger : certains jobs sur GitHub-hosted, d'autres sur votre runner.
jobs:
test:
runs-on: ubuntu-latest # runner GitHub-hosted
steps:
- uses: actions/checkout@v4
- run: npm test
deploy:
needs: test
runs-on: [self-hosted, production] # votre runner
steps:
- run: pm2 restart appLes tests tournent sur l'infra GitHub, le déploiement sur votre serveur. Propre.
Gérer plusieurs runners
Pour un mono-repo avec staging et production :
# Sur le serveur staging
./config.sh --url https://github.com/org/repo --token TOKEN \
--name staging-runner --labels linux,staging
# Sur le serveur production
./config.sh --url https://github.com/org/repo --token TOKEN \
--name prod-runner --labels linux,productiondeploy-staging:
runs-on: [self-hosted, staging]
deploy-production:
runs-on: [self-hosted, production]
environment: productionenvironment: production active les protection rules GitHub — vous pouvez exiger une approbation manuelle avant que le job s'exécute.
Runners éphémères avec Docker
Un runner persistant accumule de l'état entre les runs : fichiers temporaires, containers orphelins, caches corrompus. Pour les workloads sensibles, les runners éphémères sont meilleurs — chaque job démarre dans un container frais.
# Image officielle Actions Runner Controller
docker run -d --restart always \
-e GITHUB_TOKEN=ghp_votre_token \
-e RUNNER_NAME=docker-runner-$(hostname) \
-e RUNNER_REPO=https://github.com/org/repo \
-e RUNNER_LABELS=docker,ephemeral \
-v /var/run/docker.sock:/var/run/docker.sock \
ghcr.io/actions/actions-runner:latestSi vous allez loin dans cette direction, Actions Runner Controller (ARC) sur Kubernetes scale automatiquement les runners en fonction de la charge — mais c'est une autre conversation.
Sécurité : le point critique
Ne jamais connecter un self-hosted runner à un repository public. C'est la règle la plus importante. Sur un repo public, n'importe qui peut ouvrir une PR qui déclenche un workflow sur votre runner — et exécuter du code arbitraire sur votre serveur.
Règles pratiques :
# L'utilisateur github-runner n'a pas sudo
sudo visudo
# Ne PAS ajouter github-runner
# Isoler le répertoire de travail
chmod 750 /home/github-runner/actions-runner/_work
# Pas d'accès aux fichiers de configuration sensibles
chmod 600 /home/github-runner/.envLe runner tourne avec les permissions de l'utilisateur github-runner — limité, isolé. Si vous utilisez Docker dans les workflows, évitez de monter le socket Docker sur un runner partagé entre projets : un workflow mal écrit peut accéder à tous les containers de la machine.
Un self-hosted runner n'est pas la solution par défaut — le runner GitHub-hosted couvre la majorité des besoins. Mais quand votre pipeline doit toucher votre réseau privé, ou que vous déployez directement depuis le CI sans passer par SSH, installer un runner sur votre VPS prend 15 minutes et change profondément ce que vous pouvez automatiser.
Pour les bases des workflows GitHub Actions avant d'aller plus loin, GitHub Actions : automatiser les tests et le déploiement couvre la syntaxe complète avec des exemples CI/CD réels.
Si vous déployez une app Node.js depuis votre runner, héberger une app Node.js sur un VPS couvre PM2, Nginx et la configuration complète du serveur.