Stratégie de cache GitHub Actions : accélérer le pipeline CI/CD par 5

npm install : 3 minutes 15 secondes.
C’était le temps de build CI du projet que j’ai repris l’année dernière. À chaque push, je regardais les logs GitHub Actions tourner, attendant la coche verte. Honnêtement, je passais souvent sur une autre fenêtre — autant attendre.
Après avoir ajouté le cache, le même build se termine en 40 secondes. Environ 5 fois plus rapide.
Ce n’est pas de la magie : c’est une bonne configuration de la stratégie de cache GitHub Actions. Dans cet article, je regroupe les pièges que j’ai rencontrés, les données testées et des modèles de configuration prêts à copier. Si vous attendez aussi vos builds CI, cet article pourrait vous faire gagner pas mal de temps.
1. Concepts clés du mécanisme de cache
Comprenez d’abord comment le cache fonctionne, sinon la configuration devient piégeuse.
Le mécanisme de cache GitHub Actions est simple — trois étapes : recherche → restauration → sauvegarde. Vous définissez une key, GitHub cherche une correspondance parmi tous les caches. Trouvé ? Restauration directe dans votre répertoire de travail. Pas trouvé ? Sauvegarde d’une nouvelle entrée à la fin du job.
Quelques limites strictes à connaître :
| Limite | Valeur |
|---|---|
| Plafond de cache par dépôt | 10 Go |
| Taille max d’un fichier de cache | 5 Go (au-delà de 1 Go, les problèmes apparaissent) |
| Durée de rétention | Suppression après 7 jours sans accès |
| Limite globale d’upload concurrent | 5 caches uploadés simultanément max |
J’ai vu des gens tomber dans le piège des 10 Go — trop de dépendances, le cache grossit, les nouveaux caches ne peuvent plus être stockés, les anciens sont supprimés, chaque build repart à froid.
Un point souvent confondu : Cache et Artifact ne sont pas la même chose. Le Cache sert le CI, il vise la vitesse ; l’Artifact est pour les humains — artefacts de build, rapports de tests, conservation longue durée. Le Cache a une limite de 10 Go, l’Artifact n’a pas de plafond (mais consomme le stockage du dépôt).
Il y a aussi le Docker Layer Cache, dédié aux builds Docker, avec une logique différente du cache classique — on en parle plus loin.
2. Stratégie de conception des clés de cache
Le succès du cache dépend entièrement de la bonne conception de la key. C’est le cœur de toute stratégie.
Qu’est-ce que hashFiles()
GitHub fournit la fonction intégrée hashFiles(), qui calcule le hash d’un fichier. Souvent utilisée sur package-lock.json ou yarn.lock — dépendances inchangées, hash inchangé, cache hit.
key: npm-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
Cela génère une clé du type npm-Linux-a1b2c3d4e5f6.... Tant que package-lock.json ne change pas, la clé reste identique.
restore-keys : plan de secours
Mais les dépendances finissent par évoluer, d’où restore-keys. C’est un mécanisme de correspondance dégradée :
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-{{ runner.os }}-
Priorité à la key complète. Pas de match ? Recherche d’un ancien cache commençant par npm-Linux-. Ce n’est pas un hit exact, mais la majorité des paquets dans node_modules sont déjà là — installation incrémentale des nouvelles dépendances seulement.
Comparaison de trois modèles de nommage
Après mes tests, voici les trois modèles recommandés :
Mode simple (petits projets) :
key: {{ runner.os }}-node-{{ hashFiles('**/package-lock.json') }}
Mode version (plusieurs versions de Node) :
key: {{ runner.os }}-node{{ matrix.node-version }}-{{ hashFiles('**/package-lock.json') }}
Mode multi-chemins (monorepo) :
key: {{ runner.os }}-{{ hashFiles('**/package-lock.json', '**/yarn.lock') }}
Comment vérifier si le cache est hit
actions/cache expose une variable cache-hit :
- uses: actions/cache@v4
id: cache-npm
with:
path: ~/.npm
key: {{ runner.os }}-node-{{ hashFiles('**/package-lock.json') }}
- name: Check cache hit
run: echo "Cache hit - {{ steps.cache-npm.outputs.cache-hit }}"
true = hit exact, false = hit partiel ou miss complet. Vous pouvez conditionner npm ci :
- name: Install dependencies
if: steps.cache-npm.outputs.cache-hit != 'true'
run: npm ci
3. Exemples de configuration en production
La théorie est faite, passons au code. Configurations testées en conditions réelles, prêtes à copier.
Cache npm (setup-node recommandé)
setup-node intègre déjà le cache, plus simple que actions/cache manuel :
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # ou 'yarn', 'pnpm'
Une ligne suffit. Pour cacher d’autres répertoires (ex. node_modules), il faut actions/cache :
- uses: actions/cache@v4
with:
path: node_modules
key: {{ runner.os }}-nm-{{ hashFiles('**/package-lock.json') }}
restore-keys: {{ runner.os }}-nm-
Mon conseil : privilégier le cache intégré de setup-node, sauf besoin spécifique.
yarn et pnpm
Le répertoire de cache yarn diffère de npm :
- uses: actions/cache@v4
with:
path: |
~/.yarn/cache
~/.yarn/install-state.gz
key: yarn-{{ runner.os }}-{{ hashFiles('**/yarn.lock') }}
pnpm utilise un store global :
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: pnpm-{{ runner.os }}-{{ hashFiles('**/pnpm-lock.yaml') }}
Cache Python/pip
Chemins de cache pour les projets Python :
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-{{ runner.os }}-{{ hashFiles('**/requirements.txt') }}
restore-keys: pip-{{ runner.os }}-
Docker Layer Cache
Les builds Docker sont les plus longs. BuildKit supporte le backend de cache GitHub Actions :
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
push: false
cache-from: type=gha
cache-to: type=gha,mode=max
type=gha stocke les layers Docker via le service de cache GitHub Actions. En test, un build d’image de 5 minutes descend à environ 1 minute.
Cache des modules Go
- uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: go-{{ runner.os }}-{{ hashFiles('**/go.sum') }}
Cache Rust Cargo
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-{{ runner.os }}-{{ hashFiles('**/Cargo.lock') }}
La compilation Rust est lente — le cache fait gagner beaucoup de temps. Attention : le répertoire target grossit — nettoyage régulier recommandé.
4. Optimisation des performances et bonnes pratiques
Données testées et pièges rencontrés, pour éviter les mêmes erreurs.
Données de référence
D’après le rapport de test RunsOn (mise à jour janvier 2026), avec un cache bien configuré :
| Opération | Sans cache | Avec cache | Gain |
|---|---|---|---|
| npm install | 3 minutes | 40 secondes | ~5× |
| yarn install | 2 min 30 s | 35 secondes | ~4× |
| Docker build | 5 minutes | 1 minute | ~5× |
| pip install | 45 secondes | 8 secondes | ~5× |
Taux de hit entre 70 et 90 %, selon la qualité de la stratégie de clés.
Pièges courants
Ne pas cacher node_modules directement
C’est ce que j’ai fait au début — grosse erreur.
# À ne pas faire
path: node_modules
node_modules est spécifique à la plateforme — paquets installés sous Linux, problèmes possibles sous Windows. Mieux vaut cacher le répertoire global (~/.npm) et laisser npm ci assembler.
Cache cross-OS : GNU tar + zstd
Le tar par défaut diffère entre macOS et Windows, échec de restauration possible. Ajoutez :
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
enableCrossOsArchive: true
Pollution du cache
Parfois le cache contient des dépendances corrompues et le build échoue en boucle. Solutions :
- Suppression manuelle : dépôt GitHub → Actions → Caches → Supprimer
- Forcer une nouvelle key : ajouter un préfixe ou un horodatage
key: npm-v2-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
Checklist des bonnes pratiques
Points clés à vérifier avant configuration :
- Privilégier le cache intégré des actions officielles (setup-node, setup-python)
- Inclure hashFiles dans la key, sinon les dépendances mises à jour utilisent encore l’ancien cache
- Définir restore-keys — la correspondance dégradée sauve la mise
- Ne pas cacher node_modules, cacher les répertoires globaux
- Nettoyer régulièrement les caches expirés, éviter de dépasser 10 Go
5. Questions fréquentes
Q1 : Pourquoi mon taux de hit est-il faible ?
Cause la plus fréquente : une key qui change trop souvent. Horodatage ou nom de branche dans la key → nouvelle key à chaque push. Solution : utiliser uniquement runner.os et hashFiles, retirer les variables superflues.
Autre cause : hashFiles matche trop de fichiers. Ex. hashFiles('**/*.json') — un changement de config invalide le cache. Limitez à package-lock.json ou yarn.lock.
Q2 : Que faire si l’espace cache dépasse la limite ?
10 Go paraît large, mais un monorepo ou le cache Docker sature vite. Solutions :
- Nettoyage régulier : GitHub Actions → Caches, suppression manuelle
- Caches séparés : keys distinctes par type de dépendance
- Self-hosted runners : pas de limite 10 Go
Q3 : Configuration spéciale pour les self-hosted runners ?
Non, le mécanisme est identique. Avantage : cache local, pas de latence réseau, restauration plus rapide. Inconvénient : pas de nettoyage automatique — script planifié nécessaire.
Q4 : Comment forcer la mise à jour du cache ?
Modifier la key. Ajouter un préfixe de version :
key: npm-v3-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
Ou supprimer l’ancien cache pour une régénération complète.
Conclusion
En résumé : bien utiliser le cache, et le CI peut être 5 fois plus rapide.
Un calcul rapide — 2 minutes économisées par build, 10 exécutions par jour, 600 minutes par mois, soit environ 10 heures. De quoi écrire plusieurs articles.
Si vous débutez avec GitHub Actions, commencez par le cache intégré de setup-node, une ligne suffit. Quand vous atteignez un plafond, revenez aux stratégies de clés avancées et au Docker Layer Cache.
Cet article est le 3e de la série GitHub Actions en production. J’ai déjà couvert la mise en place du pipeline CI et les stratégies de déploiement — consultez les articles précédents si ça vous intéresse.
Au prochain push, regardez votre temps de build. De 3 minutes à 40 secondes — à vous de tester.
Configurer le cache GitHub Actions pour accélérer le CI/CD
En configurant le cache GitHub Actions, réduire le temps de build npm install de 3 minutes à 40 secondes
⏱️ Estimated time: 10 min
- 1
Step 1: Choisir une stratégie de cache
Choisir la stratégie de cache selon le gestionnaire de paquets du projet :
• Projet npm : privilégier le cache intégré de setup-node
• Projet yarn/pnpm : configurer les chemins de cache
• Build Docker : utiliser le backend gha de BuildKit - 2
Step 2: Concevoir les clés de cache
Utiliser hashFiles() basé sur le fichier de verrouillage pour générer des clés stables :
• Modèle de base : {{ runner.os }}-node-{{ hashFiles('**/package-lock.json') }}
• Ajouter restore-keys comme correspondance de secours
• Éviter d'inclure un horodatage ou le nom de branche dans la key - 3
Step 3: Ajouter la configuration de cache
Ajouter les étapes de cache dans le fichier workflow :
• npm : utiliser actions/setup-node@v4 avec cache: 'npm'
• Chemin personnalisé : utiliser actions/cache@v4
• Docker : définir cache-from et cache-to - 4
Step 4: Vérifier l'efficacité du cache
Vérifier si le cache est hit :
• Consulter la variable de sortie cache-hit (true = hit exact)
• Comparer les temps de build (réduction attendue de 4 à 5 fois)
• Vérifier la page Actions → Caches pour confirmer le stockage - 5
Step 5: Maintenir le cache régulièrement
Éviter les problèmes de cache :
• Surveiller l'utilisation de l'espace cache (limite 10 Go)
• Nettoyer régulièrement les anciens caches
• En cas de pollution, mettre à jour le préfixe de la key pour forcer une reconstruction
FAQ
Pourquoi mon taux de hit du cache n'est que de 30 % ?
Que se passe-t-il si le cache dépasse 10 Go ?
• Séparer les caches par type de dépendance (npm, Docker, pip avec des keys distinctes)
• Supprimer manuellement les caches inutiles via Actions → Caches
• Pour un monorepo, envisager des dépôts séparés ou des self-hosted runners
Les branches différentes peuvent-elles partager le cache ?
En quoi le cache diffère-t-il sur un self-hosted runner ?
Un échec de restauration du cache interrompt-il le build ?
Comment savoir si le cache doit être mis à jour ?
• Changement de version des dépendances : hashFiles gère automatiquement, sans intervention manuelle
• Pollution du cache : le build échoue soudainement, supprimer l'ancien cache
• Changement de configuration : ex. mise à niveau de Node, ajouter le numéro de version dans la key
Dans la plupart des cas, une configuration correcte évite toute gestion manuelle.
7 min de lecture · Publié le: 7 avr. 2026 · Mis à jour le: 27 juil. 2026
Guide complet GitHub Actions
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Pipeline CI GitHub Actions : construire build et tests automatisés de zéro
Guide pratique pour mettre en place un pipeline CI GitHub Actions : configuration du workflow, tests parallèles en Matrix multi-versions, optimisation du cache et astuces terrain pour automatiser build et tests.
Partie 2 sur 10
Suivant
Matrix GitHub Actions : tests parallèles multi-versions en pratique
Tutoriel pratique du Matrix GitHub Actions : syntaxe de base, filtres exclude/include, optimisation fail-fast et contrôle des ressources max-parallel, avec un modèle complet de pipeline de tests parallèles multi-versions.
Partie 4 sur 10



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire