Aller au contenu
CI/CD & Automatisation medium

Cache Node.js/npm dans GitHub Actions

9 min de lecture

Les projets Node.js ont souvent des centaines de dépendances. Sans cache, chaque npm ci télécharge tout depuis le registre npm, 30 à 60 secondes minimum. Avec le cache, c'est réduit à quelques secondes.

  • Activer le cache intégré de setup-node pour npm, pnpm et yarn
  • Configurer actions/cache quand vous avez besoin de contrôle
  • Cacher les builds Next.js, Turborepo et ESLint
  • Gérer un monorepo à workspaces
  • Éviter les pièges du cache de node_modules et de pnpm

La méthode la plus simple :

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'npm' # Détecte automatiquement package-lock.json
- run: npm ci

setup-node détecte le gestionnaire de paquets selon les fichiers présents :

Fichier présentGestionnaire détecté
package-lock.jsonnpm
pnpm-lock.yamlpnpm
yarn.lockyarn
# Pour pnpm
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install
# Pour yarn
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'yarn'
- run: yarn install --frozen-lockfile

Pour plus de contrôle ou des cas spécifiques :

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
- name: Get npm cache directory
id: npm-cache-dir
shell: bash
run: echo "dir=$(npm config get cache)" >> "$GITHUB_OUTPUT"
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- run: npm ci
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'yarn'
- run: yarn install --immutable

Pour des gains maximum, cachez directement node_modules :

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
- name: Cache node_modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
id: cache-node-modules
with:
path: node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci

Au-delà des dépendances, les outils de build maintiennent leur propre cache sur disque. Le préserver d'un run à l'autre accélère franchement la compilation.

- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
.next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
restore-keys: |
nextjs-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-
nextjs-${{ runner.os }}-
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}-
turbo-${{ runner.os }}-
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: .eslintcache
key: eslint-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- run: npm run lint -- --cache
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# Aucun droit par défaut : le job demande le minimum
permissions: {}
jobs:
build:
runs-on: ubuntu-24.04
permissions:
contents: read
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Test
run: npm test
- name: Build
run: npm run build
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: '**/package-lock.json' # Tous les lockfiles
- run: npm ci --workspaces
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 9
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm -r build # Build tous les packages

Certains cas particuliers, modules natifs, tests end-to-end, ont leurs propres caches à connaître.

Pour les packages avec compilation native (sharp, bcrypt, etc.) :

- name: Cache native modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
~/.npm
~/.node-gyp
key: native-${{ runner.os }}-node${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
- name: Cache Cypress
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.cache/Cypress
key: cypress-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}

Deux problèmes reviennent souvent avec le cache Node.js. Voici comment les reconnaître et les corriger.

Fenêtre de terminal
npm ERR! `npm ci` can only install packages when your package.json and package-lock.json are in sync

La clé de cache doit inclure le hash du lockfile :

# ✅ Correct
key: npm-${{ hashFiles('**/package-lock.json') }}
# Assurez-vous d'installer pnpm avant setup-node
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
cache: 'pnpm'
  • Le cache intégré de setup-node (cache: 'npm' / 'pnpm' / 'yarn') couvre la majorité des projets.
  • Pour pnpm, installez pnpm/action-setup avant setup-node, sinon le store reste introuvable.
  • Cacher node_modules directement est plus rapide mais fragile : les scripts postinstall sont sautés sur un cache hit.
  • Cachez les builds (.next/cache, .turbo, .eslintcache) en plus des dépendances pour le gain maximal.
  • En monorepo, cache-dependency-path: '**/package-lock.json' couvre tous les workspaces.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn