Cache e artifact resolvem problemas diferentes no GitLab CI. O primeiro acelera o download de uma dependência que o job consegue refazer. O segundo transporta uma saída produzida pelo pipeline, como dist/, para um job que precisa testá-la ou publicá-la.
Essa diferença aparece quando o job de build termina, mas o job seguinte não encontra a pasta dist. A solução rápida costuma ser colocar tudo em cache. Isso pode deixar o pipeline verde em um runner e quebrar no próximo, porque cache é uma otimização que pode desaparecer. O problema é de contrato: o pipeline precisa recuperar um resultado produzido ou apenas evitar baixar a mesma dependência de novo?
A regra prática é curta: use cache para dependências que o job consegue recriar. Use artifacts para arquivos produzidos por um job e necessários em outro, como dist/, relatórios de teste ou cobertura. A documentação de cache do GitLab CI faz essa mesma separação e explica por que a disponibilidade do cache depende do runner e da configuração de armazenamento.
Este é um recorte do guia de GitLab CI para Node.js. O guia maior mostra a estrutura de um pipeline; aqui, a pergunta é qual mecanismo deve carregar cada arquivo entre jobs. A decisão também complementa o uso de npm ci no CI, porque um cache bom acelera a instalação sem transformar o resultado do build em algo opcional.
Não medi a duração de um pipeline específico. Os YAMLs são exemplos ilustrativos e precisam ser ajustados ao runner, ao gerenciador de pacotes e aos diretórios que o projeto realmente produz.
Resposta curta
- Cache guarda dependências ou outros arquivos que podem ser refeitos quando não estão disponíveis.
- Artifact guarda uma saída do pipeline que um job posterior precisa receber, inspecionar ou publicar.
- Use a chave do cache baseada no lockfile para evitar reutilizar dependências de outro estado.
- Use
expire_inem artifacts temporários e um registry para releases que precisam sobreviver ao pipeline.- Se o pipeline não pode continuar sem o arquivo, não trate esse arquivo como cache.
| Pergunta | Cache | Artifact |
|---|---|---|
| Se faltar | Refaça ou baixe o conteúdo de novo | Verifique o contrato entre produtor e consumidor |
| Uso típico em Node.js | .npm/ e dependências reutilizáveis |
dist/, relatórios e logs |
| Duração | Substituível e baseada nas entradas | Ligado ao pipeline e a expire_in |
Cache acelera uma operação que já sabe se refazer
Cache é uma cópia reutilizável, não a fonte de verdade do pipeline. O GitLab descreve cache como arquivos que um job baixa e salva para que execuções posteriores não precisem baixá-los outra vez. Um cache pode não existir no runner seguinte, pode ter sido limpo ou pode ter uma chave diferente. O job que depende dele ainda precisa saber reconstruir o conteúdo.
Em um projeto Node.js, o diretório .npm/ é um candidato melhor que node_modules/. O primeiro guarda o conteúdo que o npm pode reutilizar durante uma instalação. O segundo contém uma árvore já extraída que pode variar com a versão do Node, o sistema operacional, scripts de instalação e a configuração do runner. Isso não proíbe cachear node_modules, mas aumenta o que a chave e o ambiente precisam garantir.
A chave deve acompanhar a entrada que muda o resultado. Para dependências npm, o package-lock.json costuma ser uma entrada útil. O GitLab permite gerar a chave a partir do conteúdo de arquivos com cache:key:files, além de separar caches por branch ou por outro contexto do pipeline, como explica a documentação já citada.
default:
image: node:22
cache:
key:
files:
- package-lock.json
paths:
- .npm/
install:
stage: build
script:
- npm ci --cache .npm --prefer-offline
Esse exemplo ainda funciona sem cache. O npm ci baixa as dependências novamente quando .npm/ não estiver disponível. A chave baseada no lockfile reduz a chance de uma mudança no manifesto continuar usando uma cópia que pertence a outro estado do projeto.
Artifact transporta a saída que o próximo job precisa
Artifact é a saída arquivada por um job e disponibilizada para outros jobs ou para inspeção. A documentação de job artifacts do GitLab lista build output, relatórios e arquivos ligados a um pipeline como casos de uso. Se o job de deploy precisa receber dist/ exatamente como foi produzido pelo job de build, esse diretório é um artifact.
Um artifact também é uma boa fronteira para relatórios de teste, cobertura, logs de diagnóstico e arquivos que um revisor precisa baixar. Se o relatório vem de uma suíte Playwright, o guia sobre estado compartilhado no Playwright no CI ajuda a corrigir a causa do teste instável antes de guardar o resultado. Defina expire_in quando o arquivo é temporário. Para uma versão que precisa de URL estável, ser consumida por outros projetos ou sobreviver à vida do pipeline, use um package registry ou outro armazenamento de release, não um artifact tratado como publicação 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
O dependencies do exemplo limita de qual job o segundo job baixa artifacts. Jobs em estágios posteriores podem receber artifacts de jobs anteriores por padrão, e o GitLab também oferece needs:artifacts para declarar essa relação com mais precisão. Escolha uma forma e verifique o pipeline real, porque o nome do job, os estágios e a topologia de needs mudam o fluxo.
Como separar os dois no mesmo pipeline?
Um pipeline pode usar cache e artifact no mesmo job, desde que eles representem coisas diferentes. O cache acelera npm ci; o artifact leva dist/ ao job que executará testes sobre o build ou fará o deploy. A mesma pasta não deve representar os dois papéis: o próprio GitLab alerta que cache é restaurado antes de artifacts e pode ser sobrescrito quando ambos usam o mesmo caminho.
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
O YAML é ilustrativo. dist/index.js e scripts/deploy.sh precisam existir no projeto, e node:22 precisa ser compatível com a versão declarada pela aplicação. A verificação test -f é pequena, mas evita que um job de deploy esconda a ausência do output atrás de uma mensagem genérica.
Se o job de verificação recompila tudo a partir do checkout, ele pode não precisar do artifact de build. Nesse caso, remova a dependência em vez de copiar arquivos sem uma razão. O artifact só é útil quando testa ou entrega a saída que o pipeline quer preservar.
Como diagnosticar um cache que “some”?
Um cache ausente não é automaticamente uma falha de correção. A primeira pergunta é se o job consegue reconstruí-lo. Se consegue, observe estes pontos:
- Runner diferente. Caches locais podem não acompanhar o job quando outro runner executa a etapa. O GitLab documenta a necessidade de um runner compartilhado ou de cache distribuído para cenários com múltiplos runners.
- Chave ampla demais. A chave
nodepode reaproveitar conteúdo entre estados incompatíveis. Prefira uma chave que inclua o lockfile ou outro identificador que represente a dependência. - Chave ampla demais em outro sentido. Dois jobs podem gravar diretórios diferentes no mesmo cache. Um upload pode substituir o arquivo de cache que o outro job esperava.
- Arquitetura diferente. Dependências nativas ou binários compilados podem não ser portáveis entre sistemas e arquiteturas. A cacheabilidade não transforma um binário em universal.
- Mesmo caminho para cache e artifact. Cache e artifact podem se sobrepor e a ordem de restauração produzir um resultado diferente do esperado.
Se a ausência do cache deixa o job correto e apenas mais lento, registre isso como um problema de desempenho. Se a ausência impede o pipeline de validar o commit, o arquivo provavelmente é um artifact, um pacote versionado ou uma dependência explícita, não um cache.
O que muda quando o artifact está faltando?
Artifact ausente costuma ser uma falha de contrato. Verifique se o job produtor terminou com sucesso, se o caminho existe dentro do diretório do projeto e se o consumidor pede o artifact do job certo. Também confira dependencies, needs:artifacts, rules e o estágio, porque uma relação declarada de forma restritiva pode impedir o download.
O arquivo de configuração do GitLab CI é a referência para a sintaxe desses campos. Não deduza o comportamento apenas pelo nome needs: leia a configuração do job e confirme no log se o runner baixou o artifact antes do script.
Quando o output é um release, não resolva o erro aumentando o tempo de expiração de um artifact indefinidamente. A documentação do GitLab separa artifacts temporários, outputs ligados a um commit e arquivos de release que precisam de um registry ou de uma URL estável. Essa distinção mantém o pipeline responsável pelo resultado da execução sem transformar o histórico de jobs em um repositório de versões.
Checklist para decidir antes de adicionar uma chave
Pergunte sobre o arquivo, não sobre a palavra que parece mais conveniente:
- O job consegue recriá-lo sem mudar o resultado esperado? Se sim, cache é possível.
- Ele é uma saída produzida por este pipeline e necessária em outro estágio? Use artifact.
- O arquivo precisa ser baixado por alguém para revisar uma falha? Artifact ou relatório.
- Ele precisa existir por semanas, ter uma URL estável ou ser consumido por outro projeto? Use um registry ou armazenamento de release.
- A chave muda quando o lockfile, a imagem, a arquitetura ou a versão relevante muda?
- O pipeline continua correto quando o cache está vazio?
- O consumidor baixa apenas os artifacts que realmente usa?
- O caminho não está configurado simultaneamente como cache e artifact?
Essa última pergunta é fácil de esquecer quando uma configuração cresce por cópia. Um pipeline legível mostra o que pode faltar e o que precisa acompanhar a execução. O ganho de velocidade vem do cache; a continuidade do resultado vem do artifact.
Como este artigo foi produzido
Samuel Fajreldines é o autor responsável por este texto. A pesquisa combinou a documentação atual do GitLab CI com uma leitura de discussões públicas sobre cache, runners e artifacts. Os exemplos foram revisados como YAML ilustrativo, mas não foram executados em um projeto GitLab real. Portanto, o artigo não mede tempo de pipeline, custo de armazenamento ou comportamento de um runner específico.
Usei assistência de IA para organizar fontes, comparar a cobertura existente e revisar a consistência entre as versões de idioma. A assistência não executou um pipeline, não mediu disponibilidade de cache e não substitui a verificação da configuração no seu runner.