Rust com Docker: Imagem Oficial e Multi-stage Build

Guia completo de Rust com Docker: imagem oficial, multi-stage builds para imagens pequenas, cargo-chef para cache e otimização para CI/CD.

O Docker é uma ferramenta essencial para criar ambientes de desenvolvimento reproduzíveis e deploys consistentes. Neste guia, vamos explorar como usar o Rust com Docker, desde a imagem oficial até multi-stage builds otimizados para produção.

Imagem Oficial do Rust

A equipe Rust mantém imagens oficiais no Docker Hub:

# Imagem completa com Rust stable
docker pull rust:latest

# Versão específica
docker pull rust:1.83

# Baseada em Alpine (menor)
docker pull rust:alpine

# Baseada em Debian slim
docker pull rust:slim

Comparação de Tamanhos

ImagemTamanhoUso
rust:latest~1.4 GBDesenvolvimento, CI completo
rust:slim~700 MBCI otimizado
rust:alpine~500 MBBuilds musl estáticos

Executar Rust em um Container

Para testar rapidamente:

docker run --rm -it rust:latest bash
rustc --version
cargo new hello && cd hello && cargo run

Para montar seu projeto local:

docker run --rm -it -v $(pwd):/app -w /app rust:latest cargo build

Dockerfile Básico

Para um projeto Rust simples:

FROM rust:1.83

WORKDIR /app
COPY . .

RUN cargo build --release

CMD ["./target/release/meu-app"]

Problema: Essa imagem é grande (~1.4 GB) e não aproveita cache de dependências. Vamos melhorar isso com multi-stage builds.

Multi-stage Build (Recomendado)

O multi-stage build compila em uma imagem grande e copia apenas o binário para uma imagem mínima:

# Etapa 1: Compilação
FROM rust:1.83 AS builder

WORKDIR /app
COPY . .
RUN cargo build --release

# Etapa 2: Imagem final
FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/target/release/meu-app /usr/local/bin/meu-app

EXPOSE 8080
CMD ["meu-app"]

Resultado: A imagem final tem ~80-100 MB em vez de ~1.4 GB.

Build Estático com Musl (Imagem Minimal)

Para uma imagem ainda menor usando Alpine:

# Etapa 1: Compilação estática
FROM rust:1.83-alpine AS builder

RUN apk add --no-cache musl-dev

WORKDIR /app
COPY . .
RUN cargo build --release --target x86_64-unknown-linux-musl

# Etapa 2: Imagem scratch (mínima possível)
FROM scratch

COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/meu-app /meu-app

EXPOSE 8080
ENTRYPOINT ["/meu-app"]

Resultado: A imagem final contém apenas o binário (~5-20 MB).

Para mais informações sobre compilação estática, veja o guia de Cross-Compilation em Rust.

Otimizar Cache com cargo-chef

O maior problema de Docker com Rust é a recompilação de todas as dependências quando o código-fonte muda. O cargo-chef resolve isso separando o build de dependências do build do código:

# Etapa 1: Planejar
FROM rust:1.83 AS chef
RUN cargo install cargo-chef
WORKDIR /app

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

# Etapa 2: Compilar dependências (cached)
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --release --recipe-path recipe.json

# Etapa 3: Compilar o projeto
COPY . .
RUN cargo build --release

# Etapa 4: Imagem final
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/meu-app /usr/local/bin/meu-app
CMD ["meu-app"]

Vantagem: Se apenas o código-fonte mudar (sem novas dependências), as etapas 1 e 2 usam cache e a reconstrução é muito mais rápida.

Docker Compose para Desenvolvimento

Para um ambiente de desenvolvimento com hot-reload:

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - cargo-cache:/usr/local/cargo/registry
      - target-cache:/app/target
    ports:
      - "8080:8080"
    command: cargo watch -x run

volumes:
  cargo-cache:
  target-cache:
# Dockerfile.dev
FROM rust:1.83

RUN cargo install cargo-watch

WORKDIR /app

Otimizar para CI/CD

Para usar Docker em pipelines de CI como o GitHub Actions, aplique estas otimizações:

Usar BuildKit

DOCKER_BUILDKIT=1 docker build -t meu-app .

Cache de Registry

# Monte o cache do Cargo como cache de build
FROM rust:1.83 AS builder
WORKDIR /app
COPY . .
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/app/target \
    cargo build --release && \
    cp target/release/meu-app /meu-app

GitHub Actions com Docker

- name: Build Docker image
  uses: docker/build-push-action@v5
  with:
    context: .
    push: true
    tags: meu-registro/meu-app:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

.dockerignore

Sempre inclua um .dockerignore para evitar copiar arquivos desnecessários:

target/
.git/
.gitignore
Dockerfile
docker-compose.yml
*.md
.env

Solução de Problemas

Build muito lento no Docker

Use cargo-chef para cache de dependências e BuildKit para cache de camadas. Certifique-se de que o .dockerignore exclui o diretório target/.

Erro: “binary not found” na imagem final

Verifique se o COPY usa o caminho correto e se o target de compilação é compatível com a imagem final:

# Se a imagem final é Debian, compile para glibc (padrão)
# Se a imagem final é Alpine, compile para musl

Binário compilado no Alpine não roda no Debian (ou vice-versa)

Alpine usa musl libc, enquanto Debian usa glibc. Use a mesma base para build e runtime, ou compile estaticamente.

Próximos Passos

Com Docker e Rust configurados:

Se você trabalha com Go e Docker, confira o golang.com.br para comparar padrões de Dockerfile entre as duas linguagens.


Última atualização: 23 de fevereiro de 2026. Para gerenciar versões do Rust, consulte o Guia Completo do Rustup.