Estrategia de caché en GitHub Actions: acelera tu pipeline CI/CD hasta 5 veces

npm install: 3 minutos y 15 segundos.
Ese era el tiempo de build CI de un proyecto que asumí el año pasado. Cada push, miraba los logs de GitHub Actions dando vueltas, esperando el tick verde. La verdad es que a menudo cambiaba de ventana a otra cosa — total, había que esperar.
Después añadí caché. El mismo build, 40 segundos. Unas 5 veces más rápido.
No es magia: es configurar bien la estrategia de caché en GitHub Actions. En este artículo recopilo los errores que cometí, los datos que probé y plantillas de configuración listas para copiar. Si también esperas builds CI, esto puede ahorrarte bastante tiempo de café.
1. Conceptos centrales del mecanismo de caché
Primero entiende cómo funciona la caché; si no, es fácil tropezar al configurarla.
El mecanismo de caché en GitHub Actions es bastante simple — tres pasos: buscar → restaurar → guardar. Defines una key, GitHub busca si hay coincidencia entre todas las cachés. Si la encuentra, restaura directamente en tu directorio de trabajo; si no, al terminar la tarea guarda una nueva.
Pero hay límites duros que debes conocer:
| Límite | Valor |
|---|---|
| Tope de caché por repositorio | 10 GB |
| Tope por archivo de caché | 5 GB (por encima de 1 GB suelen aparecer problemas) |
| Plazo de retención | Eliminación tras 7 días sin acceso |
| Límite global de subidas concurrentes | Máximo 5 cachés subiendo a la vez |
He visto gente caer en el límite de 10 GB — demasiadas dependencias, la caché crece y crece, al final la nueva no cabe, la antigua se borra y cada build es un «arranque en frío».
Otro punto que confunde: Cache y Artifact no son lo mismo. Cache es para CI, busca velocidad; Artifact es para personas, p. ej. artefactos de build o informes de tests, con retención larga. Cache tiene límite de 10 GB; Artifact no tiene tope (pero consume almacenamiento del repo).
También está Docker Layer Cache, específico para builds Docker, con lógica distinta a la caché normal; lo veremos aparte más adelante.
2. Estrategia de diseño de claves de caché
Si la caché acierta depende enteramente de si la key está bien diseñada. Es el núcleo de toda la estrategia.
Qué es hashFiles()
GitHub ofrece la función integrada hashFiles(), que calcula el hash de archivos. Se usa mucho en package-lock.json o yarn.lock — si las dependencias no cambian, el hash tampoco y la caché acierta.
key: npm-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
Esto genera una clave del tipo npm-Linux-a1b2c3d4e5f6.... Mientras package-lock.json no cambie, la clave tampoco.
restore-keys: plan B
Pero las dependencias se actualizan, y ahí entra restore-keys. Es un mecanismo de «coincidencia degradada»:
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
restore-keys: |
npm-{{ runner.os }}-
Prioriza la key completa. ¿No coincide? Busca cachés antiguas que empiecen por npm-Linux-. No es acierto total, pero la mayoría de paquetes en node_modules ya están; solo instalas dependencias nuevas de forma incremental.
Comparativa de tres patrones de nomenclatura
Tras probar, recomiendo estos tres:
Modo simple (proyectos pequeños):
key: {{ runner.os }}-node-{{ hashFiles('**/package-lock.json') }}
Modo versión (varias versiones de Node):
key: {{ runner.os }}-node{{ matrix.node-version }}-{{ hashFiles('**/package-lock.json') }}
Modo multi-ruta (monorepo):
key: {{ runner.os }}-{{ hashFiles('**/package-lock.json', '**/yarn.lock') }}
Cómo saber si la caché acertó
actions/cache expone la 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 = acierto exacto; false = acierto parcial o miss total. Puedes usar esa variable para decidir si ejecutar npm ci:
- name: Install dependencies
if: steps.cache-npm.outputs.cache-hit != 'true'
run: npm ci
3. Ejemplos de configuración en la práctica
Teoría hecha; al código. Estas configuraciones las he probado y puedes copiarlas directamente.
Caché npm (recomendado: setup-node)
setup-node ya trae caché integrada, más limpia que actions/cache manual:
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # o 'yarn', 'pnpm'
Una línea. Si quieres cachear otros directorios (p. ej. node_modules), sigue haciendo falta actions/cache:
- uses: actions/cache@v4
with:
path: node_modules
key: {{ runner.os }}-nm-{{ hashFiles('**/package-lock.json') }}
restore-keys: {{ runner.os }}-nm-
Mi consejo: prioriza la caché integrada de setup-node, salvo necesidades especiales.
yarn y pnpm
El directorio de caché de yarn difiere de npm:
- uses: actions/cache@v4
with:
path: |
~/.yarn/cache
~/.yarn/install-state.gz
key: yarn-{{ runner.os }}-{{ hashFiles('**/yarn.lock') }}
pnpm es más especial: usa 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') }}
Caché Python/pip
Rutas de caché para proyectos Python:
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-{{ runner.os }}-{{ hashFiles('**/requirements.txt') }}
restore-keys: pip-{{ runner.os }}-
Docker Layer Cache
Los builds Docker son lo más lento. La buena noticia: BuildKit soporta el backend de caché de 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 usa el servicio de caché de GitHub Actions para capas Docker. En pruebas, un build de imagen de 5 minutos baja a unos 60 segundos.
Caché de módulos Go
- uses: actions/cache@v4
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: go-{{ runner.os }}-{{ hashFiles('**/go.sum') }}
Caché Cargo (Rust)
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: cargo-{{ runner.os }}-{{ hashFiles('**/Cargo.lock') }}
Rust compila lento; la caché ahorra mucho tiempo. Cuidado: el directorio target crece — conviene limpiarlo periódicamente.
4. Optimización de rendimiento y mejores prácticas
Recopilo datos probados y errores que cometí, para que evites los mismos.
Datos de referencia de rendimiento
Según el informe de pruebas de RunsOn (actualizado en enero de 2026), con caché bien configurada:
| Operación | Sin caché | Con caché | Mejora |
|---|---|---|---|
| npm install | 3 min | 40 s | ~5× |
| yarn install | 2 min 30 s | 35 s | ~4× |
| Docker build | 5 min | 1 min | ~5× |
| pip install | 45 s | 8 s | ~5× |
La tasa de acierto ronda el 70-90%, según qué tan bien diseñes las claves.
Trampas habituales
No cachees node_modules directamente
Al principio lo hice así y fue un desastre.
# No hagas esto
path: node_modules
node_modules es específico de plataforma — paquetes instalados en Linux pueden fallar en Windows. Lo correcto es cachear el directorio global (~/.npm) y dejar que npm ci monte el árbol.
Caché multi-OS: GNU tar + zstd
El tar por defecto en macOS y Windows usa formatos distintos y la restauración falla. Añade:
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
enableCrossOsArchive: true
Contaminación de caché
A veces la caché guarda dependencias rotas y el build falla una y otra vez. Solución:
- Borrar manualmente: en el repo, Actions → Caches, eliminar
- Forzar nueva key: añade prefijo o marca de tiempo para regenerar
key: npm-v2-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
Lista de mejores prácticas
Resumen final; revísala antes de configurar:
- Prioriza la caché integrada de actions oficiales (setup-node, setup-python)
- La key debe incluir hashFiles, si no, al actualizar dependencias seguirás usando la caché antigua
- Escribe restore-keys — la coincidencia degradada salva builds
- No cachees node_modules, cachea directorios globales
- Limpia cachés caducadas periódicamente, evita superar 10 GB
5. Preguntas frecuentes
P1: ¿Por qué mi tasa de acierto es baja?
La causa más común es una key que cambia demasiado. Por ejemplo, incluir marca de tiempo o nombre de rama: cada push genera una key nueva. Solución: solo runner.os y hashFiles, sin variables innecesarias.
Otra causa: hashFiles coincide con archivos que no debería. Si escribes hashFiles('**/*.json'), un cambio en config invalida la caché. Limita a package-lock.json o yarn.lock.
P2: ¿Qué hacer si se supera el espacio de caché?
10 GB parece mucho, pero monorepos o caché Docker lo agotan rápido. Soluciones:
- Limpieza periódica: GitHub Actions → Caches, borrar las antiguas
- Cachés separadas: distintas keys por tipo de dependencia
- Self-hosted runners: sin límite de 10 GB
P3: ¿Los self-hosted runners requieren configuración especial?
No; el mecanismo es el mismo. Ventaja: la caché es local, sin latencia de red, restauración más rápida. Inconveniente: no se limpia sola; necesitas scripts programados.
P4: ¿Cómo forzar actualización de caché?
Cambia la key. Añade un prefijo de versión:
key: npm-v3-{{ runner.os }}-{{ hashFiles('**/package-lock.json') }}
O borra la caché antigua y deja que el sistema regenere.
Resumen
En una frase: con buena caché, el CI puede ir 5 veces más rápido.
Una cuenta rápida — si cada build ahorra 2 minutos y corres 10 al día, en un mes son 600 minutos, unas 10 horas. Tiempo de sobra para escribir varios artículos.
Si acabas de empezar con GitHub Actions, empieza por la caché integrada de setup-node: una línea basta. Cuando llegues al límite, vuelve aquí para claves más complejas y Docker Layer Cache.
Este artículo es el n.º 3 de la serie Guía práctica de GitHub Actions. Antes escribí sobre montaje de pipelines CI y estrategias de despliegue; si te interesa, revisa artículos anteriores.
En el próximo push, mira el tiempo de build. ¿De 3 minutos a 40 segundos? Pruébalo y lo verás.
Configurar caché en GitHub Actions para acelerar CI/CD
Configura la caché en GitHub Actions para reducir el tiempo de npm install de 3 minutos a 40 segundos
⏱️ Estimated time: 10 min
- 1
Step 1: Elegir el esquema de caché
Elige el esquema según el gestor de paquetes del proyecto:
• Proyectos npm: prioriza la caché integrada de setup-node
• Proyectos yarn/pnpm: configura las rutas de caché
• Builds Docker: usa el backend gha de BuildKit - 2
Step 2: Diseñar las claves de caché
Usa hashFiles() basado en el archivo de bloqueo para generar claves estables:
• Patrón básico: {{ runner.os }}-node-{{ hashFiles('**/package-lock.json') }}
• Añade restore-keys como respaldo de coincidencia
• Evita incluir marcas de tiempo o nombres de rama en la key - 3
Step 3: Añadir la configuración de caché
Añade pasos de caché en el archivo workflow:
• npm: usa actions/setup-node@v4 con cache: 'npm'
• Rutas personalizadas: usa actions/cache@v4
• Docker: configura cache-from y cache-to - 4
Step 4: Verificar el efecto de la caché
Comprueba si la caché acierta:
• Revisa la variable de salida cache-hit (true = coincidencia exacta)
• Compara tiempos de build (deberían reducirse 4-5 veces)
• Consulta Actions → Caches para confirmar que la caché se almacenó - 5
Step 5: Mantenimiento periódico de la caché
Evita problemas de caché:
• Monitoriza el uso de espacio (límite 10 GB)
• Limpia cachés antiguas periódicamente
• Si hay contaminación, actualiza el prefijo de la key para forzar reconstrucción
FAQ
¿Por qué mi tasa de acierto de caché es solo del 30%?
¿Qué pasa si la caché supera los 10 GB?
• Separa la caché por tipo de dependencia (npm, Docker, pip, cada una con su key)
• Elimina manualmente cachés inútiles en Actions → Caches
• En monorepos, considera repositorios separados o self-hosted runners
¿Las ramas distintas pueden compartir caché?
¿En qué se diferencia la caché en self-hosted runners?
¿Un fallo al restaurar la caché interrumpe el build?
¿Cómo saber si la caché necesita actualizarse?
• Cambio de versiones de dependencias: hashFiles lo gestiona automáticamente, sin intervención manual
• Contaminación de caché: el build falla de repente y hay que limpiar la caché antigua
• Cambio de configuración: p. ej. actualización de Node, hay que incluir la versión en la key
En la mayoría de casos, con la configuración correcta no hace falta gestionarla manualmente.
8 min de lectura · Publicado el: 7 abr 2026 · Actualizado el: 21 ago 2026
Guía completa de GitHub Actions
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Pipeline CI con GitHub Actions: automatiza build y tests desde cero
Guía práctica para montar un pipeline CI con GitHub Actions: configuración de workflows, tests en paralelo con Matrix, optimización de caché y trucos para automatizar build y tests rápidamente.
Parte 2 de 10
Siguiente
Matrix en GitHub Actions: pruebas paralelas multi-versión en la práctica
Tutorial de Matrix en GitHub Actions: sintaxis básica, filtros exclude/include, optimización fail-fast y control max-parallel, con plantilla completa de pipeline de pruebas multi-versión.
Parte 4 de 10



Comentarios
Inicia sesión con GitHub para dejar un comentario