Prendre rendez-vous
  1. Accueil
  2. /
  3. Blog
  4. /
  5. GitHub Actions self-hosted runners : déployer et sécuriser vos runners on-premise

DevOps
Infrastructure

GitHub Actions self-hosted runners : déployer et sécuriser vos runners on-premise

16 janvier 2026

10 min de lecture

Sommaire
Plan
Pourquoi des runners self-hosted ?
Installation et configuration
Sécurisation : isolation et permissions
Auto-scaling avec Kubernetes
Gestion des secrets et credentials
Monitoring et maintenance
Troubleshooting
Bonnes pratiques
Checklist déploiement
Conclusion

Les GitHub Actions runners hébergés ont des limitations (2000 minutes/mois, pas d'accès ressources internes, packages limités). Les self-hosted runners donnent le contrôle total, de meilleures performances et des coûts réduits. Guide complet de déploiement et sécurisation.

Plan

  • Pourquoi des runners self-hosted ?
  • Installation et configuration
  • Sécurisation : isolation et permissions
  • Auto-scaling avec Kubernetes
  • Gestion des secrets et credentials
  • Monitoring et maintenance
  • Troubleshooting
  • Conclusion

Pourquoi des runners self-hosted ?

Limitations des GitHub-hosted runners

Contraintes techniques :

  • 2000 minutes/mois gratuit (puis $0.008/min)
  • 6h max par job
  • RAM limitée : 7 GB
  • CPU : 2 cores
  • Stockage : 14 GB SSD
  • Pas d'accès réseau interne

Cas d'usage nécessitant self-hosted :

  • Builds lourds (>6h, >7GB RAM, GPU)
  • Accès aux bases de données internes
  • Artefacts volumineux
  • Environnements spécifiques (architectures ARM, outils custom)
  • Coûts (>10,000 min/mois)
Avantages self-hosted

✅ Performance :

  • Hardware dédié (32+ GB RAM, 16+ cores)
  • SSD local rapide
  • Cache persistent entre builds

✅ Coûts :

  • Gratuit après infra
  • ROI si >250h/mois

✅ Sécurité :

  • Isolation réseau
  • Accès ressources internes
  • Compliance (RGPD, SOC2)

✅ Flexibilité :

  • OS custom
  • Outils pré-installés
  • GPU pour ML

Installation et configuration

Installation simple (VM Linux)
# 1. Créer dossier runner
mkdir -p /opt/actions-runner && cd /opt/actions-runner

# 2. Télécharger runner
RUNNER_VERSION="2.311.0"
curl -o actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
  -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz

# 3. Extraire
tar xzf actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz

# 4. Configurer
# Obtenir token depuis GitHub :
# Repo > Settings > Actions > Runners > New self-hosted runner
./config.sh \
  --url https://github.com/your-org/your-repo \
  --token YOUR_REGISTRATION_TOKEN \
  --name my-runner-1 \
  --labels linux,x64,custom \
  --work _work

# 5. Installer service systemd
sudo ./svc.sh install
sudo ./svc.sh start

# Vérifier
sudo ./svc.sh status
Configuration niveau organisation

Pour runners partagés entre plusieurs repos :

# Token organisation (au lieu de repo)
# GitHub Org > Settings > Actions > Runners > New runner

./config.sh \
  --url https://github.com/your-org \
  --token YOUR_ORG_TOKEN \
  --name org-runner-1 \
  --labels linux,x64,org-shared
Configuration avec Docker
# Dockerfile
FROM ubuntu:22.04

# Installer dépendances
RUN apt-get update && apt-get install -y \
    curl \
    git \
    jq \
    build-essential \
    libssl-dev \
    libffi-dev \
    python3 \
    python3-pip \
    docker.io

# Créer user runner
RUN useradd -m -s /bin/bash runner

# Télécharger runner
WORKDIR /home/runner
RUN curl -o actions-runner-linux-x64.tar.gz \
    -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz \
    && tar xzf actions-runner-linux-x64.tar.gz \
    && chown -R runner:runner /home/runner

USER runner

# Script de démarrage
COPY start.sh /home/runner/start.sh
RUN chmod +x /home/runner/start.sh

ENTRYPOINT ["/home/runner/start.sh"]
# start.sh
#!/bin/bash
./config.sh \
  --url ${GITHUB_URL} \
  --token ${GITHUB_TOKEN} \
  --name ${RUNNER_NAME} \
  --labels ${RUNNER_LABELS} \
  --unattended \
  --replace

./run.sh
# Build et run
docker build -t github-runner .

docker run -d \
  --name runner-1 \
  -e GITHUB_URL="https://github.com/your-org/your-repo" \
  -e GITHUB_TOKEN="YOUR_TOKEN" \
  -e RUNNER_NAME="docker-runner-1" \
  -e RUNNER_LABELS="linux,docker" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  github-runner

Sécurisation : isolation et permissions

Principe : éphémère et isolé

❌ NE JAMAIS :

  • Runner permanent réutilisé
  • Runner avec accès root
  • Secrets hardcodés dans runner
  • Partage runner entre repos publics/privés

✅ TOUJOURS :

  • Runner éphémère (1 job = 1 container détruit)
  • User non-root
  • Secrets via GitHub Secrets
  • Isolation réseau
Runner éphémère avec Docker
# .github/workflows/build.yml
name: Build

on: [push]

jobs:
  build:
    # Utiliser label custom
    runs-on: [self-hosted, linux, ephemeral]

    container:
      image: ubuntu:22.04

    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: |
          apt-get update
          apt-get install -y build-essential
          make build

      - name: Test
        run: make test
Isolation réseau
# Firewall : bloquer tout sauf GitHub + ressources internes
ufw default deny outgoing
ufw default deny incoming

# GitHub IPs (récupérer depuis API)
curl https://api.github.com/meta | jq -r '.actions[]' | while read ip; do
    ufw allow out to $ip port 443
done

# Ressources internes (DB, registry)
ufw allow out to 10.0.1.0/24 port 5432  # PostgreSQL
ufw allow out to 10.0.2.0/24 port 5000  # Docker registry

# SSH admin uniquement
ufw allow in from 10.0.0.0/24 to any port 22

ufw enable
User non-root
# Créer user dédié
sudo useradd -m -s /bin/bash runner
sudo usermod -aG docker runner  # Si Docker nécessaire

# Installer runner en tant que runner user
sudo -u runner bash
cd /home/runner
# ... installation runner
# Dockerfile avec user non-root
FROM ubuntu:22.04

RUN useradd -m -u 1000 runner
USER runner

# ... reste installation
Limiter permissions Docker
# /etc/docker/daemon.json
{
  "userns-remap": "runner",
  "no-new-privileges": true
}

sudo systemctl restart docker

Auto-scaling avec Kubernetes

Actions Runner Controller (ARC)

Installation avec Helm :

# Ajouter repo Helm
helm repo add actions-runner-controller \
  https://actions-runner-controller.github.io/actions-runner-controller
helm repo update

# Créer namespace
kubectl create namespace actions-runner-system

# Créer secret avec GitHub token
kubectl create secret generic controller-manager \
  -n actions-runner-system \
  --from-literal=github_token=YOUR_GITHUB_PAT

# Installer controller
helm install actions-runner-controller \
  actions-runner-controller/actions-runner-controller \
  --namespace actions-runner-system \
  --set authSecret.github_token=YOUR_GITHUB_PAT
RunnerDeployment avec auto-scaling
# runner-deployment.yaml
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  name: example-runner
  namespace: default
spec:
  replicas: 2
  template:
    spec:
      repository: your-org/your-repo
      labels:
        - kubernetes
        - linux

      # Ressources
      resources:
        limits:
          cpu: '2'
          memory: '4Gi'
        requests:
          cpu: '1'
          memory: '2Gi'

      # Volumes pour cache
      volumeMounts:
        - name: docker-cache
          mountPath: /var/lib/docker

      volumes:
        - name: docker-cache
          emptyDir: {}

---
# HorizontalRunnerAutoscaler
apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata:
  name: example-runner-autoscaler
spec:
  scaleTargetRef:
    name: example-runner

  minReplicas: 1
  maxReplicas: 10

  # Scaler selon nombre de jobs en queue
  metrics:
    - type: TotalNumberOfQueuedAndInProgressWorkflowRuns
      repositoryNames:
        - your-org/your-repo

  # Scaler selon pourcentage utilisation
  scaleUpThreshold: '0.75'
  scaleDownThreshold: '0.25'
  scaleUpFactor: '2'
  scaleDownFactor: '0.5'
# Appliquer
kubectl apply -f runner-deployment.yaml

# Vérifier
kubectl get runners
kubectl get pods -l app=example-runner
Runner avec DinD (Docker-in-Docker)
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  name: docker-runner
spec:
  replicas: 3
  template:
    spec:
      repository: your-org/your-repo
      labels:
        - kubernetes
        - dind

      # Image avec Docker
      image: summerwind/actions-runner-dind:latest

      dockerdWithinRunnerContainer: true

      resources:
        limits:
          cpu: '4'
          memory: '8Gi'

Gestion des secrets et credentials

GitHub Secrets (recommandé)
# .github/workflows/deploy.yml
name: Deploy

on: [push]

jobs:
  deploy:
    runs-on: [self-hosted, linux]

    steps:
      - uses: actions/checkout@v4

      - name: Deploy to prod
        env:
          # Secrets injectés par GitHub
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
          API_KEY: ${{ secrets.API_KEY }}
        run: |
          ./deploy.sh

Créer secrets :

  • Repo > Settings > Secrets and variables > Actions > New repository secret
HashiCorp Vault (avancé)
jobs:
  deploy:
    runs-on: [self-hosted, linux]

    steps:
      - name: Get secrets from Vault
        uses: hashicorp/vault-action@v2
        with:
          url: https://vault.company.com
          token: ${{ secrets.VAULT_TOKEN }}
          secrets: |
            secret/data/prod/db password | DB_PASSWORD ;
            secret/data/prod/api key | API_KEY

      - name: Deploy
        env:
          DB_PASSWORD: ${{ env.DB_PASSWORD }}
        run: ./deploy.sh
OIDC avec cloud providers
# Authentification AWS sans credentials
jobs:
  deploy:
    runs-on: [self-hosted, linux]

    permissions:
      id-token: write
      contents: read

    steps:
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
          aws-region: us-east-1

      - name: Deploy to S3
        run: aws s3 sync ./dist s3://my-bucket/

Monitoring et maintenance

Prometheus metrics
# ServiceMonitor pour ARC
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: actions-runner-controller
  namespace: actions-runner-system
spec:
  selector:
    matchLabels:
      app: actions-runner-controller
  endpoints:
    - port: metrics
      interval: 30s

Métriques clés :

  • github_runner_jobs_completed_total
  • github_runner_jobs_failed_total
  • github_runner_jobs_duration_seconds
  • github_runner_api_rate_limit_remaining
Alertes Prometheus
# alerts.yaml
groups:
  - name: github-runners
    rules:
      - alert: RunnerJobsFailing
        expr: rate(github_runner_jobs_failed_total[5m]) > 0.1
        for: 10m
        annotations:
          summary: 'GitHub runner jobs failing'

      - alert: NoRunnersAvailable
        expr: github_runner_available == 0
        for: 5m
        annotations:
          summary: 'No GitHub runners available'

      - alert: GitHubAPIRateLimitLow
        expr: github_runner_api_rate_limit_remaining < 100
        for: 5m
        annotations:
          summary: 'GitHub API rate limit low'
Logs et debug
# Logs runner (systemd)
sudo journalctl -u actions.runner.* -f

# Logs runner (Docker)
docker logs -f runner-1

# Logs ARC (Kubernetes)
kubectl logs -n actions-runner-system \
  -l app=actions-runner-controller -f

# Debug job spécifique
# GitHub > Repo > Actions > Job > Re-run with debug logging
Rotation et updates
#!/bin/bash
# update-runners.sh - Mise à jour rolling runners

RUNNER_VERSION="2.311.0"

# Télécharger nouvelle version
cd /opt/actions-runner
curl -o actions-runner-new.tar.gz \
  -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz

# Pour chaque runner
for i in {1..5}; do
    RUNNER_DIR="/opt/actions-runner/runner-${i}"

    # Arrêter runner
    sudo systemctl stop actions.runner.runner-${i}

    # Backup
    mv $RUNNER_DIR ${RUNNER_DIR}.bak

    # Installer nouvelle version
    mkdir -p $RUNNER_DIR
    tar xzf actions-runner-new.tar.gz -C $RUNNER_DIR

    # Restaurer config
    cp ${RUNNER_DIR}.bak/.runner $RUNNER_DIR/
    cp ${RUNNER_DIR}.bak/.credentials $RUNNER_DIR/

    # Redémarrer
    sudo systemctl start actions.runner.runner-${i}

    # Attendre 30s avant next
    sleep 30
done

echo "Update complete"

Troubleshooting

Problème : Runner ne se connecte pas
# Vérifier service
sudo systemctl status actions.runner.*

# Vérifier logs
sudo journalctl -u actions.runner.* -n 100

# Vérifier connectivité GitHub
curl -v https://api.github.com

# Tester token
curl -H "Authorization: token YOUR_PAT" \
  https://api.github.com/user
Problème : Jobs timeout ou stuck
# Vérifier jobs en cours
ps aux | grep Runner.Listener

# Kill jobs stuck
sudo systemctl restart actions.runner.*

# Vérifier espace disque
df -h /opt/actions-runner/_work

# Nettoyer workspace
rm -rf /opt/actions-runner/_work/*
Problème : Out of memory
# Vérifier utilisation mémoire
free -h
docker stats

# Augmenter swap
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Problème : Docker permission denied
# Vérifier user dans groupe docker
groups runner

# Ajouter au groupe
sudo usermod -aG docker runner

# Redémarrer service
sudo systemctl restart actions.runner.*

Bonnes pratiques

✅ Sécurité
  1. Runners éphémères : 1 job = 1 container/VM détruit
  2. User non-root : jamais de runner avec sudo
  3. Isolation réseau : firewall strict
  4. Secrets externes : GitHub Secrets ou Vault
  5. Audit logs : monitoring actions runner
✅ Performance
  1. Cache persistent : Docker layers, npm/pip cache
  2. Hardware adapté : SSD, RAM suffisante
  3. Pools de runners : labels par type (small/large/gpu)
  4. Auto-scaling : ARC sur Kubernetes
  5. Warm pools : runners pré-warmés
✅ Maintenance
  1. Updates réguliers : runner tous les mois
  2. Monitoring : Prometheus + Grafana
  3. Alertes : jobs failed, no runners available
  4. Backup config : .runner et .credentials
  5. Documentation : runbooks pour troubleshooting

Checklist déploiement

✅ Installation :

  • Runner téléchargé et configuré
  • Service systemd ou Docker running
  • Runner visible dans GitHub UI
  • Labels configurés

✅ Sécurité :

  • User non-root
  • Firewall configuré
  • Pas de secrets hardcodés
  • Runners éphémères si possible
  • Isolation réseau

✅ Performance :

  • RAM >= 8GB
  • CPU >= 4 cores
  • SSD local
  • Cache Docker configuré

✅ Monitoring :

  • Prometheus metrics activés
  • Alertes configurées
  • Logs centralisés
  • Dashboard Grafana

✅ Maintenance :

  • Script update automatique
  • Backup config
  • Rotation runners
  • Documentation troubleshooting

Conclusion

Les self-hosted runners donnent des gains de performance, de la flexibilité et le contrôle total sur votre CI/CD. L'auto-scaling Kubernetes avec ARC gère des centaines de runners sans intervention manuelle.

Quand utiliser self-hosted :

  • ✅ Builds > 6h ou >7GB RAM
  • ✅ Accès ressources internes
  • ✅ > 10,000 minutes/mois
  • ✅ Hardware spécifique (GPU, ARM)
  • ✅ Compliance strict

Gains typiques :

  • Coûts : -70-90 % si >250h/mois
  • Performance : +50-200 % (hardware dédié)
  • Builds parallèles : illimités (vs 20-60 GitHub)
  • Cache : -30-60 % build time

Actions prioritaires :

  1. Déployer 2-3 runners test
  2. Configurer labels par type
  3. Implémenter runners éphémères
  4. Activer monitoring Prometheus
  5. Automatiser updates
Besoin d'aide sur ce sujet ?

Notre équipe d'experts est là pour vous accompagner dans vos projets d'infrastructure et d'infogérance.

Contactez-nous

Articles similaires

Packer + Ansible : pipeline d'images golden modernes
DevOps
Infrastructure
Conteneurs

Packer + Ansible : pipeline d'images golden modernes

Construire des images VM et conteneurs immuables avec HashiCorp Packer et Ansible. Multi-cloud, multi-format, signature, intégration CI/CD, retours ops.

29 mai 2026

Lire plus

OpenTofu : migrer depuis Terraform sans douleur
DevOps
Infrastructure

OpenTofu : migrer depuis Terraform sans douleur

OpenTofu est le fork open source de Terraform sous Linux Foundation après le passage de Terraform en BSL. Compatibilité, fonctionnalités exclusives (state encryption), procédure de migration depuis Terraform 1.5.

18 mai 2026

Lire plus

Coolify : un PaaS open source pour remplacer Heroku, Vercel ou Netlify
DevOps
Cloud
Infrastructure

Coolify : un PaaS open source pour remplacer Heroku, Vercel ou Netlify

Coolify est une alternative open source self-hosted à Heroku, Vercel et Netlify. Déploiement Git push, base de données managées, 280+ services en un clic. Architecture, déploiement, comparaison Dokku.

16 mai 2026

Lire plus


SHPV, votre partenaire de confiance en infrastructure et infogérance informatique en France.

SHPV
Prendre rendez-vousNous contacter
Expertise
InfrastructureDatacenterInfogéranceCloudHébergementTransit IP
Légales
Conditions Générales de VenteCPS - Contrat de ServicesCPS - Hébergement CloudCPS - Microsoft 365Accord sous-traitance RGPDTarifs interventions

SHPV © 2026 - Tous droits réservés

Mentions légalesPolitiques de confidentialité
SHPV FRANCE - SAS au capital de 16 000 € - 52 Rue Romain Rolland, 71230 Saint-Vallier - SIRET n°80886287400035 - R.C.S. Chalon-sur-Saône. Par téléphone 09 72 310 818 - Email: support@shpv.fr