---
title: "cargo-chef: Cache de Builds Docker em Rust | Rust Brasil"
url: "https://rustlang.com.br/blog/cargo-chef-cache-docker-rust-2026/"
markdown_url: "https://rustlang.com.br/blog/cargo-chef-cache-docker-rust-2026.MD"
description: "Aprenda a usar cargo-chef em Rust para cachear dependências no Docker, acelerar rebuilds, trabalhar com workspaces e otimizar pipelines de CI/CD modernos."
date: "2026-07-27"
author: "Equipe Rust Brasil"
---

# cargo-chef: Cache de Builds Docker em Rust | Rust Brasil

Aprenda a usar cargo-chef em Rust para cachear dependências no Docker, acelerar rebuilds, trabalhar com workspaces e otimizar pipelines de CI/CD modernos.


**O `cargo-chef` é uma das formas mais práticas de evitar que o Docker recompile todas as dependências de um projeto Rust a cada alteração em `src/`.** O fluxo separa o build em três etapas: `cargo chef prepare` gera uma receita com os manifests, `cargo chef cook` compila as dependências em uma camada cacheável e `cargo build` compila o código da aplicação. Se `Cargo.toml` e `Cargo.lock` não mudarem, o Docker pode reaproveitar a etapa mais cara.

Para começar rapidamente, instale a ferramenta com `cargo install cargo-chef --locked` e use um Dockerfile com stages de **planner**, **builder** e **runtime**. O cargo-chef não promete acelerar o primeiro build nem substitui o cache do BuildKit ou o `sccache`; ele resolve um problema específico: tornar a camada de dependências estável mesmo quando os arquivos-fonte mudam.

Este guia explica **como usar cargo-chef em Rust**, como ele funciona com Cargo workspaces, quais arquivos invalidam o cache, como combinar a ferramenta com BuildKit e como decidir se ela realmente vale a pena no seu pipeline.

## Por que o cache Docker comum funciona mal com Rust

Um Dockerfile ingênuo costuma copiar o repositório inteiro antes de compilar:

```dockerfile
FROM rust:slim AS builder
WORKDIR /app

COPY . .
RUN cargo build --release
```

Cada instrução cria uma camada. O problema é que `COPY . .` muda quando qualquer arquivo do contexto muda. Uma alteração pequena em `src/main.rs` invalida essa camada e todas as seguintes, incluindo `cargo build --release`.

O Cargo possui seu próprio cache em `target/` e nos diretórios de registry e Git, mas o build tradicional acima não oferece ao Docker uma fronteira estável entre **dependências** e **código da aplicação**. O resultado pode ser uma recompilação muito maior do que a mudança justificaria.

Em ecossistemas nos quais as dependências são declaradas em um único arquivo, é comum copiar primeiro o manifesto, instalar dependências e só depois copiar o código. Em Rust, reproduzir isso manualmente fica complicado porque um projeto pode incluir:

- `Cargo.toml` e `Cargo.lock` na raiz;
- vários membros de um [Cargo workspace](/blog/cargo-workspaces-monorepos-rust-2026/);
- targets em `src/bin/`;
- exemplos, benches e testes;
- arquivos `build.rs`;
- dependências locais por `path`;
- configuração de features e profiles.

Criar arquivos-fonte fictícios para convencer o Cargo a compilar apenas dependências é possível, mas frágil. O cargo-chef automatiza essa representação.

## Como o cargo-chef funciona

O fluxo possui dois comandos principais.

### 1. cargo chef prepare

O comando `prepare` examina a estrutura do projeto e grava uma receita:

```bash
cargo chef prepare --recipe-path recipe.json
```

O `recipe.json` descreve os manifests e os targets necessários para reproduzir a resolução do projeto sem depender do conteúdo real da aplicação. Esse arquivo vira a entrada estável da próxima camada.

### 2. cargo chef cook

O comando `cook` usa a receita para compilar as dependências:

```bash
cargo chef cook --release --recipe-path recipe.json
```

Depois disso, o Docker copia o repositório real e executa o build final:

```bash
cargo build --release
```

Como as dependências já foram compiladas na camada anterior, o build final tende a concentrar o trabalho nas crates modificadas do próprio projeto.

A ideia pode ser resumida assim:

| Etapa | Entrada principal | Quando o cache costuma ser invalidado |
|---|---|---|
| `prepare` | Manifests e estrutura dos targets | Mudança em manifests, membros ou targets |
| `cook` | `recipe.json` | Mudança na receita, toolchain, target ou flags |
| `cargo build` | Código-fonte completo | Qualquer mudança relevante no código |
| runtime | Binário e arquivos necessários | Novo artefato produzido pelo builder |

O cargo-chef não é um serviço de cache. Ele reorganiza o build para que o mecanismo de layers do Docker consiga trabalhar melhor.

## Dockerfile completo com cargo-chef

Abaixo está uma base para uma aplicação Rust que usa glibc e roda em Debian slim:

```dockerfile
# syntax=docker/dockerfile:1

FROM rust:slim AS chef
RUN cargo install cargo-chef --locked
WORKDIR /app

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json

COPY . .
RUN cargo build --release --locked --bin minha-api

FROM debian:bookworm-slim AS runtime
RUN apt-get update \
    && apt-get install -y --no-install-recommends ca-certificates \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY --from=builder /app/target/release/minha-api /usr/local/bin/minha-api

USER 10001:10001
ENTRYPOINT ["/usr/local/bin/minha-api"]
```

Substitua `minha-api` pelo nome real do binário. Para um package com nome diferente do executável, confira as seções `[[bin]]` do `Cargo.toml` ou use `cargo metadata`.

Alguns detalhes desse exemplo são importantes:

- `cargo-chef` é instalado uma vez no stage-base `chef`;
- planner e builder usam a mesma imagem e a mesma toolchain;
- somente `recipe.json` entra antes de `cargo chef cook`;
- o código real é copiado depois da camada de dependências;
- `--locked` impede uma resolução diferente da registrada no lockfile;
- a imagem final não contém `rustc`, Cargo nem o código-fonte;
- a aplicação não roda como root.

Para uma visão mais ampla sobre imagem final, musl, certificados e multi-stage build, consulte o guia de [Rust com Docker em produção](/blog/rust-docker-builds-otimizados-producao-2026/).

## Crie um .dockerignore antes de medir

Um contexto de build desnecessariamente grande deixa o envio para o daemon mais lento e pode invalidar layers por arquivos irrelevantes. Um ponto de partida:

```dockerignore
.git
.github
.gitea
target
Dockerfile*
docker-compose*.yml
.env
.env.*
*.log
README.md
```

Não copie essa lista sem revisar. Se o build usa um arquivo excluído — uma migration SQL embutida, um asset, um certificado de desenvolvimento ou metadata gerada — ele precisa continuar disponível.

O item mais importante para projetos Rust costuma ser `target`. Copiar o `target/` local para o contexto produz imagens maiores, diferenças entre máquinas e invalidações difíceis de entender. Deixe o stage builder criar seus próprios artefatos.

Também não inclua segredos no contexto. Tokens para registry privado devem entrar por mecanismos de secret do BuildKit ou da plataforma de CI, nunca por `COPY .env`.

## cargo-chef com Cargo workspaces

Em um workspace, execute o planner na raiz que contém `[workspace]`:

```dockerfile
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
```

No builder, compile o package desejado explicitamente:

```dockerfile
RUN cargo chef cook \
    --release \
    --locked \
    --recipe-path recipe.json

COPY . .
RUN cargo build --release --locked --package api --bin api
```

Há duas estratégias possíveis:

1. **cozinhar o workspace inteiro:** maximiza o reaproveitamento quando vários packages são construídos na mesma imagem, mas pode compilar dependências desnecessárias;
2. **cozinhar para um package ou conjunto específico:** reduz trabalho, porém exige que as opções de `cook` e do build final estejam alinhadas.

O ponto crítico é manter as mesmas dimensões de compilação nas duas etapas:

- profile (`--release` ou outro profile);
- target (`--target`);
- package e workspace;
- features;
- `--no-default-features`;
- toolchain;
- flags passadas por `RUSTFLAGS`;
- variáveis observadas por scripts de build.

Se você executa `cargo chef cook --release`, mas depois chama `cargo build --release --no-default-features --features postgres`, parte das dependências pode precisar ser recompilada porque o grafo efetivo mudou.

## Features, targets e cross-compilation

O comando `cook` deve receber as mesmas features usadas no build final:

```dockerfile
ARG FEATURES="postgres,rustls"

RUN cargo chef cook \
    --release \
    --locked \
    --no-default-features \
    --features "${FEATURES}" \
    --recipe-path recipe.json

COPY . .
RUN cargo build \
    --release \
    --locked \
    --no-default-features \
    --features "${FEATURES}" \
    --bin minha-api
```

Para cross-compilation, repita também o target:

```dockerfile
RUN rustup target add x86_64-unknown-linux-musl

RUN cargo chef cook \
    --release \
    --target x86_64-unknown-linux-musl \
    --recipe-path recipe.json

COPY . .
RUN cargo build \
    --release \
    --target x86_64-unknown-linux-musl \
    --bin minha-api
```

Adicionar o target ao Rust não instala automaticamente todas as ferramentas nativas necessárias. Crates com código C, geração de bindings, OpenSSL ou bibliotecas do sistema podem exigir compilador, linker, headers e packages específicos. O guia de [cross-compilation em Rust](/instalacao/cross-compilation/) ajuda a separar problemas de cache de problemas do toolchain.

## cargo-chef vs sccache vs BuildKit

As três soluções são complementares:

| Ferramenta | Nível de atuação | Principal benefício |
|---|---|---|
| cargo-chef | Estrutura das layers Docker | Mantém dependências em uma camada separada do código |
| BuildKit cache mount | Diretórios usados durante o build | Preserva registry, Git checkout e `target` entre execuções |
| sccache | Invocações do compilador | Reutiliza resultados de compilação compatíveis |
| cache remoto da CI | Exportação das layers ou mounts | Reaproveita cache entre runners efêmeros |

Um cache mount pode ser usado assim:

```dockerfile
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/usr/local/cargo/git \
    cargo chef cook --release --locked --recipe-path recipe.json
```

No build final:

```dockerfile
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/usr/local/cargo/git \
    --mount=type=cache,target=/app/target \
    cargo build --release --locked --bin minha-api \
    && cp /app/target/release/minha-api /tmp/minha-api
```

Se `target` é um mount, o artefato precisa ser copiado para um caminho fora do mount dentro da mesma instrução, como `/tmp/minha-api`, antes de ser transferido ao runtime stage. Caso contrário, a próxima etapa pode não encontrar o binário na camada resultante.

O `sccache` resolve outra parte do problema. Configure `RUSTC_WRAPPER=sccache` e um backend adequado quando o pipeline precisa reutilizar objetos de compilação entre branches ou máquinas. Leia o guia de [como reduzir o tempo de compilação do Rust](/blog/rust-tempo-compilacao-otimizar-build-2026/) para escolher entre linkador rápido, cache, Cranelift e redução de features.

## Exemplo com cache do GitHub Actions

O BuildKit consegue importar e exportar cache pela própria action de build:

```yaml
name: container

on:
  push:
    branches: [main]

jobs:
  image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - uses: docker/build-push-action@v6
        with:
          context: .
          push: false
          tags: minha-api:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
```

Em um deploy real, configure autenticação e `push: true` para o registry escolhido. Mantenha permissões mínimas e não coloque credenciais no Dockerfile.

O cargo-chef melhora a disposição das layers; `cache-from` e `cache-to` permitem que essas layers sobrevivam ao runner efêmero. Sem exportação remota, um runner descartável pode começar sem nenhum cache, independentemente da qualidade do Dockerfile.

Para uma pipeline completa com lint, testes e artefatos, veja [CI/CD para Rust](/artigos/ci-cd-rust/). Se a suíte é grande, o guia de [cargo-nextest](/blog/cargo-nextest-testes-rust-2026/) mostra como separar a otimização dos testes da otimização do build da imagem.

## O que invalida o cache

É normal que a camada de `cook` seja refeita quando algo muda no grafo de compilação. Entre os gatilhos mais comuns estão:

- alteração em `Cargo.toml` ou `Cargo.lock`;
- entrada, saída ou renomeação de um membro do workspace;
- mudança em features habilitadas;
- troca da imagem-base ou da versão do Rust;
- mudança do target ou profile;
- alteração em `RUSTFLAGS`;
- mudança em dependência local por `path`;
- modificação da estrutura de targets, como novo `src/bin/*.rs`;
- scripts `build.rs` sensíveis a arquivos ou variáveis externas;
- uso de argumentos Docker diferentes antes da camada de `cook`.

Não tente impedir toda invalidação. Se uma dependência mudou, recompilá-la é correto. O objetivo é evitar invalidações causadas apenas por uma alteração em código que não muda o grafo de dependências.

Use este diagnóstico:

```bash
docker build --progress=plain -t minha-api:test .
```

Faça uma segunda execução sem mudanças e observe quais etapas aparecem como `CACHED`. Depois altere somente um arquivo em `src/` e repita. A etapa `cargo chef cook` deve continuar cacheada; o build final deve rodar novamente.

## Armadilhas comuns

### Instalar cargo-chef duas vezes

Se planner e builder começam de imagens independentes e ambos executam `cargo install cargo-chef`, você paga a compilação da ferramenta duas vezes. Crie um stage-base `chef` e derive os dois dele, como no exemplo principal.

### Usar versões ou toolchains diferentes

Planner e builder devem operar em ambientes compatíveis. Trocar a imagem Rust entre etapas pode invalidar artefatos ou produzir recompilações. Fixe a estratégia de atualização da toolchain e faça o bump de forma intencional.

### Esquecer --locked

Sem `--locked`, o Cargo pode resolver versões diferentes quando o lockfile não está presente ou não é respeitado. Para aplicações, preserve `Cargo.lock` e use `--locked` no cook e no build final.

### Copiar o código antes de cook

Se o builder recebe `COPY . .` antes de `cargo chef cook`, uma mudança em `src/` invalida justamente a layer que deveria ser estável. Antes do `cook`, copie apenas o `recipe.json` vindo do planner.

### Esperar ganho no primeiro build

O primeiro build ainda baixa e compila dependências. Em alguns casos ele fica um pouco mais lento pela preparação da receita. Meça pelo cenário real: vários commits alterando código com manifests relativamente estáveis.

### Cachear target sem estratégia

O diretório `target` pode crescer muito e conter artefatos incompatíveis entre toolchains, targets e conjuntos de flags. Defina chaves de cache adequadas, limite retenção e não trate todo `target/` como um pacote universal.

## Quando usar e quando não usar

**cargo-chef tende a valer a pena quando:**

- a aplicação possui muitas dependências;
- imagens são reconstruídas várias vezes por dia;
- a maior parte dos commits altera `src/`, não manifests;
- o pipeline usa cache persistente de layers;
- há um workspace com serviços ou binários recorrentes;
- o tempo de build afeta diretamente o ciclo de deploy.

**Talvez não valha a complexidade quando:**

- a crate é pequena e compila rapidamente;
- cada build começa sem cache local ou remoto;
- `Cargo.toml` e `Cargo.lock` mudam em quase todo commit;
- a imagem é construída raramente;
- o pipeline já usa um cache de compilação eficaz e o gargalo está em testes, link ou download da imagem-base;
- a equipe ainda não possui um Dockerfile multi-stage correto.

Uma boa regra é começar com multi-stage build e `.dockerignore`, medir, habilitar cache remoto do BuildKit e só então adicionar cargo-chef se a compilação das dependências continuar dominando os rebuilds.

## Checklist para produção

Antes de publicar a imagem, confira:

- [ ] planner e builder usam a mesma família de imagem e toolchain;
- [ ] `cargo-chef` está disponível em `prepare` e `cook`;
- [ ] `Cargo.lock` está versionado e `--locked` é usado;
- [ ] features, target e profile coincidem entre `cook` e `build`;
- [ ] `target/`, `.git/` e segredos não entram no contexto;
- [ ] cache remoto está configurado para runners efêmeros;
- [ ] o binário é copiado para uma imagem final sem compilador;
- [ ] certificados e bibliotecas dinâmicas necessárias existem no runtime;
- [ ] o processo roda sem privilégios de root;
- [ ] testes e auditorias acontecem antes do push da imagem;
- [ ] a imagem é identificada por tag imutável ou digest no deploy;
- [ ] tempos de build são medidos em vez de estimados.

Para segurança de dependências e políticas de licença, combine o pipeline com [cargo-deny, RustSec e SBOM](/blog/rust-seguranca-supply-chain-cargo-deny-sbom-2026/). Para organizar publicação de binários e checksums, consulte [engenharia de release em Rust](/blog/rust-release-engineering-binaries-cli-servicos-2026/).

## Conclusão

O cargo-chef funciona porque cria uma fronteira de cache que o Docker não consegue inferir sozinho em projetos Rust complexos. `prepare` transforma manifests e estrutura em `recipe.json`; `cook` compila as dependências; e o build final recebe o código real. Em rebuilds com dependências estáveis, a etapa cara pode ser reaproveitada.

A ferramenta não deve ser instalada por hábito. Primeiro corrija o Dockerfile multi-stage, reduza o contexto com `.dockerignore` e garanta que a CI exporta cache. Depois compare builds frios e quentes. Se a camada de dependências permanece dominante, cargo-chef é uma solução clara, reproduzível e fácil de explicar para o time.

Dominar esse fluxo é uma competência útil para quem trabalha com backend, plataforma e DevOps em Rust. Consulte também as [vagas de Rust](/vagas/) e os [perfis de empresas que usam Rust](/empresas/) para entender onde conhecimentos de containers, CI e performance de build aparecem no mercado.

## Perguntas frequentes

### O que é cargo-chef?

cargo-chef é uma ferramenta que gera uma receita dos manifests de um projeto Rust e permite compilar dependências em uma layer Docker separada. Isso evita refazer todo o grafo quando apenas o código da aplicação muda.

### Como instalar cargo-chef?

Use:

```bash
cargo install cargo-chef --locked
```

Em Docker, prefira instalar uma vez em um stage-base compartilhado pelo planner e pelo builder.

### cargo-chef substitui sccache?

Não. cargo-chef organiza layers; sccache reutiliza resultados do compilador. É possível usar os dois, desde que o ganho justifique a operação e o armazenamento adicionais.

### cargo-chef funciona com workspaces?

Sim. Rode `prepare` na raiz do workspace e mantenha alinhados packages, features, target e profile entre `cook` e o build final.

### Por que cargo chef cook recompila tudo?

As causas mais comuns são mudança no `recipe.json`, toolchain diferente, features ou target desalinhados, ausência de cache persistente e argumentos Docker que invalidam a layer. Execute o build com `--progress=plain` para identificar exatamente onde o cache foi perdido.

## Leia também

- [Cargo: gerenciador e build system do Rust](/ecossistema/cargo/)
- [Ferramentas essenciais do Cargo](/artigos/cargo-ferramentas-essenciais/)
- [Rust e Docker: builds otimizados para produção](/blog/rust-docker-builds-otimizados-producao-2026/)
- [Como reduzir o tempo de compilação do Rust](/blog/rust-tempo-compilacao-otimizar-build-2026/)
- [Cargo workspaces e monorepos](/blog/cargo-workspaces-monorepos-rust-2026/)
- [CI/CD para projetos Rust](/artigos/ci-cd-rust/)
- [cargo-nextest para testes mais rápidos](/blog/cargo-nextest-testes-rust-2026/)
