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.

Diagrama compara cache de dependências com artifacts de build passando entre jobs do GitLab CI.

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_in em 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:

  1. 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.
  2. Chave ampla demais. A chave node pode reaproveitar conteúdo entre estados incompatíveis. Prefira uma chave que inclua o lockfile ou outro identificador que represente a dependência.
  3. 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.
  4. 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.
  5. 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.