Cache y artifacts resuelven problemas distintos en GitLab CI. El primero acelera una dependencia que el job puede reconstruir. El segundo transporta una salida del pipeline, como dist/, hasta un job que necesita probarla o publicarla.
La diferencia aparece cuando el job de build termina, pero el siguiente job no encuentra dist. La solución rápida suele ser ponerlo todo en cache. Eso puede hacer que el pipeline pase en un runner y falle en el siguiente, porque el cache es una optimización que puede no estar disponible. La pregunta del contrato es sencilla: ¿el pipeline necesita recuperar un resultado producido o solo evitar descargar otra vez la misma dependencia?
Este artículo es un complemento específico de la guía de GitLab CI para Node.js. Esa guía cubre la estructura de un pipeline; aquí la pregunta es qué mecanismo debe transportar cada archivo entre jobs. También complementa la guía sobre npm ci en CI, porque un buen cache acelera la instalación sin convertir la salida del build en algo opcional.
No medí la duración de un pipeline concreto. Los YAML son ejemplos ilustrativos y debes adaptarlos al runner, al gestor de paquetes y a los directorios que realmente produzca tu proyecto.
Respuesta corta
- Cache guarda dependencias u otros archivos que se pueden reconstruir cuando no están disponibles.
- Un artifact guarda una salida del pipeline que un job posterior necesita recibir, inspeccionar o publicar.
- Usa el lockfile para crear la clave del cache y no reutilizar dependencias de otro estado del proyecto.
- Define
expire_inpara artifacts temporales y usa un registry para releases que deban sobrevivir al pipeline.- Si el pipeline no puede continuar sin el archivo, no trates ese archivo como cache.
| Pregunta | Cache | Artifact |
|---|---|---|
| Si falta | Reconstruye o descarga el contenido otra vez | Revisa el contrato entre productor y consumidor |
| Uso típico en Node.js | .npm/ y dependencias reutilizables |
dist/, informes y logs |
| Duración | Reemplazable y basada en las entradas | Ligada al pipeline y a expire_in |
El cache acelera un trabajo que el job ya sabe repetir
El cache es una copia reutilizable, no la fuente de verdad del pipeline. GitLab describe el cache como archivos que un job descarga y guarda para que las ejecuciones posteriores no tengan que descargarlos de nuevo. Puede no existir en el siguiente runner, haberse limpiado o usar otra clave. El job que depende de él todavía necesita poder reconstruir el contenido.
En un proyecto Node.js, .npm/ suele ser un candidato mejor que node_modules/. El primer directorio guarda contenido que npm puede reutilizar durante la instalación. El segundo contiene un árbol ya extraído que puede variar con la versión de Node, el sistema operativo, los scripts de instalación y la configuración del runner. Eso no prohíbe cachear node_modules, pero aumenta lo que deben garantizar la clave y el entorno.
La clave debe seguir la entrada que cambia el resultado. Para dependencias npm, package-lock.json suele ser una entrada útil. GitLab permite crear claves a partir del contenido de archivos con cache:key:files, además de separar caches por rama u otro contexto del pipeline, como explica su documentación.
default:
image: node:22
cache:
key:
files:
- package-lock.json
paths:
- .npm/
install:
stage: build
script:
- npm ci --cache .npm --prefer-offline
Este ejemplo sigue funcionando sin cache. npm ci vuelve a descargar las dependencias cuando .npm/ no está disponible. Una clave basada en el lockfile reduce la posibilidad de que un cambio en el manifiesto siga usando una copia de otro estado del proyecto. El comportamiento y las opciones de la clave están documentados en la guía de cache de GitLab.
Un artifact transporta la salida que necesita el siguiente job
Un artifact es la salida archivada por un job y disponible para otros jobs o para inspección. La documentación de job artifacts de GitLab incluye como casos de uso el build output, los informes y los archivos ligados a un pipeline. Si un job de deploy debe recibir dist/ exactamente como lo produjo el job de build, ese directorio es un artifact.
Los artifacts también son una buena frontera para informes de tests, cobertura, logs de diagnóstico y archivos que un revisor necesita descargar. Si el informe viene de una suite de Playwright, la guía sobre estado compartido en Playwright CI ayuda a corregir la causa del test inestable antes de guardar el resultado. Define expire_in cuando el archivo sea temporal. Si una versión necesita una URL estable, debe ser consumida por otros proyectos o debe sobrevivir al pipeline, usa un package registry u otro almacenamiento de releases en lugar de tratar un artifact como publicación permanente.
build:
stage: build
script:
- npm ci --cache .npm --prefer-offline
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
test-build:
stage: test
dependencies:
- build
script:
- test -f dist/index.js
- npm test
La entrada dependencies limita qué job proporciona artifacts al segundo job. Los jobs de stages posteriores pueden descargar por defecto los artifacts de stages anteriores, y GitLab también ofrece needs:artifacts para declarar la relación con más precisión. Elige una forma y verifica el pipeline real, porque los nombres de los jobs, los stages y la topología de needs cambian el flujo.
¿Cómo se separan los dos mecanismos en el mismo pipeline?
Un pipeline puede usar cache y artifacts en el mismo job siempre que representen cosas distintas. El cache acelera npm ci; el artifact lleva dist/ al job que prueba el build o hace el deploy. La misma carpeta no debería representar ambos papeles. GitLab advierte que el cache se restaura antes que los artifacts y puede sobrescribirse cuando ambos usan la misma ruta.
stages:
- build
- verify
- deploy
default:
image: node:22
cache:
key:
files:
- package-lock.json
paths:
- .npm/
build:
stage: build
script:
- npm ci --cache .npm --prefer-offline
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
verify:
stage: verify
dependencies:
- build
script:
- test -f dist/index.js
- npm test
deploy:
stage: deploy
dependencies:
- build
script:
- ./scripts/deploy.sh dist/
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
El YAML es ilustrativo. dist/index.js y scripts/deploy.sh deben existir en el proyecto, y node:22 debe ser compatible con la versión que declare la aplicación. La comprobación test -f es pequeña, pero evita que un job de deploy oculte la ausencia del output tras un mensaje genérico.
Si el job de verificación vuelve a compilar todo desde el checkout, quizá no necesite el artifact de build. Elimina la dependencia en lugar de copiar archivos sin una razón. Un artifact es útil cuando el pipeline quiere conservar, probar o entregar esa salida.
¿Cómo diagnosticar un cache que desaparece?
Un cache ausente no es automáticamente un fallo de corrección. Primero pregunta si el job puede reconstruirlo. Si puede, revisa estos puntos:
- Runner diferente. Un cache local puede no acompañar al job cuando otro runner ejecuta el stage. GitLab documenta la necesidad de un runner compartido o de cache distribuido en configuraciones con varios runners.
- Clave demasiado amplia. Una clave como
nodepuede reutilizar contenido entre estados incompatibles. Prefiere una clave que incluya el lockfile u otro identificador de la entrada de dependencias. - Clave demasiado amplia en otro sentido. Dos jobs pueden escribir directorios distintos en el mismo cache. Una subida puede sustituir el archivo que esperaba el otro job.
- Arquitectura distinta. Las dependencias nativas o los binarios compilados pueden no ser portables entre sistemas y arquitecturas. Que algo sea cacheable no vuelve universal a un binario.
- La misma ruta para cache y artifact. Los dos mecanismos pueden solaparse y el orden de restauración puede producir un resultado distinto del esperado.
Si la ausencia del cache deja el job correcto y solo lo vuelve más lento, regístralo como un problema de rendimiento. Si su ausencia impide que el pipeline valide el commit, el archivo probablemente sea un artifact, un package versionado o una dependencia explícita, no un cache.
¿Qué cambia cuando falta un artifact?
La falta de un artifact suele ser un fallo de contrato. Comprueba que el job productor terminó correctamente, que la ruta existe dentro del directorio del proyecto y que el consumidor pide el artifact del job correcto. Revisa también dependencies, needs:artifacts, rules y el stage, porque una relación restrictiva puede impedir la descarga.
La referencia YAML de GitLab CI documenta la sintaxis de esos campos. No deduzcas el comportamiento solo por la palabra needs: lee la configuración del job y confirma en el log que el runner descargó el artifact antes de ejecutar el script.
Cuando el output es un release, no resuelvas el error ampliando indefinidamente la retención del artifact. GitLab separa artifacts temporales, outputs ligados a un commit y archivos de release que necesitan un registry o una URL estable. Esa frontera mantiene al pipeline responsable de su ejecución sin convertir el historial de jobs en un repositorio de versiones.
Checklist antes de añadir una clave
Pregunta por el archivo en lugar de elegir la palabra más cómoda:
- ¿El job puede reconstruirlo sin cambiar el resultado esperado? Si sí, el cache es posible.
- ¿Es una salida producida por este pipeline y necesaria en un stage posterior? Usa un artifact.
- ¿Alguien necesita descargarlo para investigar un fallo? Usa un artifact o un informe.
- ¿Debe existir durante semanas, tener una URL estable o ser consumido por otro proyecto? Usa un registry o almacenamiento de releases.
- ¿La clave cambia cuando cambian el lockfile, la imagen, la arquitectura o la versión relevante?
- ¿El pipeline sigue siendo correcto cuando el cache está vacío?
- ¿El consumidor descarga solo los artifacts que realmente usa?
- ¿La ruta está configurada simultáneamente como cache y artifact?
La última pregunta se olvida fácilmente cuando una configuración crece copiando bloques. Un pipeline legible muestra qué puede faltar y qué debe acompañar la ejecución. El cache aporta velocidad; el artifact conserva el resultado.
Cómo se produjo este artículo
Samuel Fajreldines es responsable de este artículo. La investigación combinó la documentación actual de GitLab CI con una revisión de discusiones públicas sobre cache, runners y artifacts. Los ejemplos se revisaron como YAML ilustrativo, pero no se ejecutaron en un proyecto GitLab real. Por tanto, el artículo no mide el tiempo del pipeline, el coste de almacenamiento ni el comportamiento de un runner específico.
Usé asistencia de IA para organizar fuentes, comparar la cobertura existente y revisar la consistencia entre las versiones de idioma. No ejecutó un pipeline, no midió la disponibilidad del cache y no sustituye la verificación de la configuración en tu runner.