---
title: "CI/CD para Rust: GitHub Actions e Deploy | Rust Brasil"
url: "https://rustlang.com.br/artigos/ci-cd-rust/"
markdown_url: "https://rustlang.com.br/artigos/ci-cd-rust.MD"
description: "Guia de CI/CD para projetos Rust: GitHub Actions, cargo-chef, Docker, testes e deploy automatizado."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# CI/CD para Rust: GitHub Actions e Deploy | Rust Brasil

Guia de CI/CD para projetos Rust: GitHub Actions, cargo-chef, Docker, testes e deploy automatizado.


## Introdução

Um pipeline de CI/CD (Continuous Integration / Continuous Deployment) bem configurado é a espinha dorsal de qualquer projeto profissional em Rust. Ele garante que cada mudança no código seja compilada, testada e analisada automaticamente antes de chegar à produção.

Rust apresenta desafios únicos para CI/CD: tempos de compilação longos, binários grandes e a necessidade de cross-compilation para diferentes plataformas. Neste artigo, vamos construir pipelines otimizados que superam esses desafios, usando GitHub Actions como plataforma principal, com Docker multi-stage builds para deploy eficiente.

## O Problema: CI Lento e Deploy Manual

### Não Faça Isso: Workflow sem Cache

```yaml
# ERRADO: .github/workflows/ci.yml — sem cache, sem otimização
name: CI
on: push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: rustup update stable
      - run: cargo build          # Baixa e compila TODAS as dependências do zero
      - run: cargo test           # Compila tudo novamente
      - run: cargo clippy         # E mais uma vez...
      # Tempo total: 15-30 minutos para cada push
```

### Não Faça Isso: Dockerfile Não Otimizado

```dockerfile
# ERRADO: Imagem gigante, recompila tudo a cada mudança
FROM rust:latest
WORKDIR /app
COPY . .
RUN cargo build --release
CMD ["./target/release/meu-app"]
# Resultado: Imagem de 2GB+, rebuild completo a cada alteração
```

## A Solução: Pipeline Otimizado e Profissional

### Faça Isso: GitHub Actions com Cache e Paralelismo

```yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-Dwarnings"

jobs:
  check:
    name: Verificar compilação
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt

      - name: Cache Cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: Verificar formatação
        run: cargo fmt --all -- --check

      - name: Clippy
        run: cargo clippy --all-targets --all-features

  test:
    name: Testes
    runs-on: ubuntu-latest
    needs: check
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Cache Cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/bin/
            ~/.cargo/registry/index/
            ~/.cargo/registry/cache/
            ~/.cargo/git/db/
            target/
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: Instalar cargo-nextest
        uses: taiki-e/install-action@nextest

      - name: Executar testes
        run: cargo nextest run --all-features

      - name: Doc tests
        run: cargo test --doc

  security:
    name: Auditoria de segurança
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  coverage:
    name: Cobertura de código
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Instalar cargo-tarpaulin
        run: cargo install cargo-tarpaulin

      - name: Gerar relatório de cobertura
        run: cargo tarpaulin --out xml --output-dir coverage

      - name: Upload cobertura
        uses: codecov/codecov-action@v4
        with:
          files: coverage/cobertura.xml
```

### Faça Isso: Testes Rápidos com cargo-nextest

`cargo-nextest` executa testes até **3x mais rápido** que `cargo test`:

```bash
# Instalar
cargo install cargo-nextest

# Executar testes (paralelo por padrão)
cargo nextest run

# Com retry para testes flaky
cargo nextest run --retries 2

# Apenas testes que falharam na última execução
cargo nextest run --run-ignored=only
```

Configure o nextest para seu projeto:

```toml
# .config/nextest.toml
[profile.default]
retries = 0
slow-timeout = { period = "60s", terminate-after = 2 }
fail-fast = true

[profile.ci]
retries = 2
fail-fast = false
```

```yaml
# No GitHub Actions
- name: Testes no CI
  run: cargo nextest run --profile ci
```

## Docker Multi-Stage Build Otimizado

### Faça Isso: Dockerfile com Cache de Dependências

```dockerfile
# Dockerfile

# ===== Stage 1: Builder =====
FROM rust:1.85-bookworm AS builder

WORKDIR /app

# Truque: copiar apenas os manifestos primeiro para cachear dependências
COPY Cargo.toml Cargo.lock ./

# Criar um projeto dummy para compilar dependências
RUN mkdir src && \
    echo "fn main() {}" > src/main.rs && \
    cargo build --release && \
    rm -rf src

# Agora copiar o código real (dependências já estão compiladas e cacheadas)
COPY src/ src/

# Tocar no main.rs para forçar recompilação apenas do nosso código
RUN touch src/main.rs && \
    cargo build --release

# ===== Stage 2: Runtime =====
FROM debian:bookworm-slim

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

# Criar usuário não-root
RUN useradd --create-home appuser
USER appuser

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

EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s \
    CMD curl -f http://localhost:8080/health || exit 1

CMD ["meu-app"]
```

**Resultado**: Imagem de ~80MB (em vez de 2GB+), e rebuilds só recompilam seu código (não as dependências).

### Faça Isso: Build Ainda Menor com Musl (Binário Estático)

```dockerfile
# Dockerfile.musl
FROM rust:1.85-bookworm AS builder

RUN rustup target add x86_64-unknown-linux-musl
RUN apt-get update && apt-get install -y musl-tools

WORKDIR /app
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs && \
    cargo build --release --target x86_64-unknown-linux-musl && \
    rm -rf src

COPY src/ src/
RUN touch src/main.rs && \
    cargo build --release --target x86_64-unknown-linux-musl

# Imagem final: ~10MB com scratch
FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/meu-app /meu-app
EXPOSE 8080
ENTRYPOINT ["/meu-app"]
```

## Cross-Compilation para Múltiplas Plataformas

### GitHub Actions para Releases Multi-Plataforma

```yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    tags: ['v*']

jobs:
  build:
    name: Build ${{ matrix.target }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            artifact: meu-app
          - target: x86_64-apple-darwin
            os: macos-latest
            artifact: meu-app
          - target: aarch64-apple-darwin
            os: macos-latest
            artifact: meu-app
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            artifact: meu-app.exe

    steps:
      - uses: actions/checkout@v4

      - name: Instalar Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Build
        run: cargo build --release --target ${{ matrix.target }}

      - name: Upload artefato
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.target }}
          path: target/${{ matrix.target }}/release/${{ matrix.artifact }}

  release:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4

      - name: Criar release
        uses: softprops/action-gh-release@v2
        with:
          files: |
            x86_64-unknown-linux-gnu/meu-app
            x86_64-apple-darwin/meu-app
            aarch64-apple-darwin/meu-app
            x86_64-pc-windows-msvc/meu-app.exe
```

### Cross-Compilation Local com `cross`

```bash
# Instalar cross (usa Docker para cross-compilation)
cargo install cross

# Compilar para Linux ARM64
cross build --release --target aarch64-unknown-linux-gnu

# Compilar para Windows a partir do Linux
cross build --release --target x86_64-pc-windows-gnu
```

## Otimização do Cache no CI

### Faça Isso: Cache Inteligente com sccache

```yaml
# .github/workflows/ci.yml — com sccache
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      SCCACHE_GHA_ENABLED: "true"
      RUSTC_WRAPPER: "sccache"
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Configurar sccache
        uses: mozilla-actions/sccache-action@v0.0.6

      - name: Build e teste
        run: |
          cargo build --release
          cargo test --release

      - name: Estatísticas do cache
        run: sccache --show-stats
```

## Armadilhas Comuns

### 1. Cache Invalidado com Frequência

```yaml
# ERRADO: Cache por hash de todo o diretório
key: ${{ runner.os }}-cargo-${{ hashFiles('**/*.rs') }}
# Qualquer mudança em qualquer .rs invalida o cache

# CORRETO: Cache por hash do Cargo.lock (dependências)
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Invalidado apenas quando dependências mudam
```

### 2. Testes que Dependem de Serviços Externos

```yaml
# CORRETO: Use service containers
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: teste
          POSTGRES_DB: app_teste
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - name: Rodar testes
        env:
          DATABASE_URL: postgres://postgres:teste@localhost:5432/app_teste
        run: cargo nextest run
```

### 3. Builds de Release sem Otimização

```toml
# Cargo.toml — certifique-se de que o profile release está otimizado
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
strip = true
```

### 4. Ignorar Testes de Documentação

```yaml
# Doc tests são testes legítimos — não os ignore
- name: Testes de documentação
  run: cargo test --doc
```

## Exemplo do Mundo Real: Pipeline Completo

```yaml
# .github/workflows/full-pipeline.yml
name: Pipeline Completo

on:
  push:
    branches: [main]
  pull_request:

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-Dwarnings"

jobs:
  # Job 1: Verificações rápidas (< 2 min)
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/
            ~/.cargo/git/
            target/
          key: ${{ runner.os }}-lint-${{ hashFiles('**/Cargo.lock') }}
      - run: cargo fmt --check
      - run: cargo clippy --all-targets --all-features

  # Job 2: Testes (paralelo ao lint)
  test:
    name: Testes (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/
            ~/.cargo/git/
            target/
          key: ${{ matrix.os }}-test-${{ hashFiles('**/Cargo.lock') }}
      - uses: taiki-e/install-action@nextest
      - run: cargo nextest run --all-features
      - run: cargo test --doc

  # Job 3: Segurança (paralelo)
  security:
    name: Segurança
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  # Job 4: Build de release (após testes)
  build:
    name: Build Release
    needs: [lint, test, security]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/
            ~/.cargo/git/
            target/
          key: ${{ runner.os }}-release-${{ hashFiles('**/Cargo.lock') }}
      - run: cargo build --release
      - uses: actions/upload-artifact@v4
        with:
          name: release-binary
          path: target/release/meu-app

  # Job 5: Deploy (após build)
  deploy:
    name: Deploy
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: release-binary
      - name: Deploy para produção
        run: echo "Implemente seu deploy aqui (Docker push, SSH, AWS, etc.)"
```

## Checklist de CI/CD para Projetos Rust

1. **Cache do Cargo** — Use `actions/cache` baseado em `Cargo.lock`
2. **cargo fmt --check** — Formatação consistente
3. **cargo clippy** — Linting com `RUSTFLAGS="-Dwarnings"`
4. **cargo nextest run** — Testes rápidos e paralelos
5. **cargo test --doc** — Não esqueça dos doc tests
6. **cargo audit** — Vulnerabilidades de segurança
7. **Docker multi-stage** — Imagens pequenas e seguras
8. **Cross-compilation** — Binários para múltiplas plataformas
9. **sccache** — Cache de compilação entre jobs
10. **Profile release otimizado** — LTO, codegen-units=1, strip

---

## Veja Também

- [Instalação: GitHub Actions](/instalacao/github-actions/) — Configure Rust no GitHub Actions
- [Instalação: Docker](/instalacao/docker/) — Rust com Docker passo a passo
- [Segurança em Rust](/artigos/seguranca-rust/) — Automatize auditorias de segurança no CI
- [Otimização de Performance](/artigos/otimizacao-performance/) — Otimize seus builds de release
- [Documentação em Rust](/artigos/documentacao-rust/) — Doc tests como parte do CI
- [Gerenciamento de Dependências](/artigos/gerenciamento-dependencias/) — Lockfile e versionamento no CI

---

Veja como configurar pipelines de CI/CD em outras linguagens:

- <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">CI/CD para projetos Go: GitHub Actions, testes e builds automatizados</a>
- <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">CI/CD em Python: tox, pytest e deploy automatizado com GitHub Actions</a>
