Como construir imagens Docker multi‑arquitetura (amd64/arm64) com GitHub Actions

Docker & Containers · Por · Publicado em · Atualizado em · 9 min de leitura

Estrutura tridimensional de cubos flutuantes em tons azul e rosa sobre fundo degradê
Ilustração abstrata que simboliza arquitetura modular e containers em diferentes plataformas Foto: Steve A Johnson / Pexels

Este tutorial mostra, passo a passo, como construir imagens Docker multi‑arquitetura (amd64 e arm64) e publicar manifestos com GitHub Actions. Você vai entender como funcionam os manifestos OCI, estruturar um Dockerfile multiplataforma, configurar Buildx e QEMU no CI, aplicar cache do BuildKit e validar que cada arquitetura puxa a imagem correta.

Multi‑arquitetura e manifestos OCI: o que você vai publicar

Uma imagem multi‑arquitetura é um conjunto de imagens específicas por plataforma reunidas sob uma mesma tag. O registro armazena um índice OCI, também chamado de manifest list.

Quando você faz o pull, o cliente escolhe a variante correta com base na plataforma. Assim, a tag única, como latest, aponta para amd64 e arm64 ao mesmo tempo, sem esforço no consumo.

No Buildx, publicar para várias plataformas em um único push já cria o manifesto automaticamente. Também é possível criar ou atualizar manifestos explicitamente quando necessário.

Pré‑requisitos de ambiente e acesso ao registro

Antes de iniciar, garanta acesso ao registro onde vai publicar e um repositório com um Dockerfile pronto para build. O runner do GitHub Actions pode ser o padrão em Linux.

O workflow instalará QEMU e Buildx para permitir builds cruzados. Separe as credenciais do registro como segredos do repositório para autenticação no pipeline.

  • Conta no Docker Hub e/ou GHCR com permissão de escrita.
  • Segredos configurados no repositório (por exemplo: DOCKERHUB_USERNAME/DOCKERHUB_TOKEN, ou uso do GITHUB_TOKEN no GHCR).
  • Dockerfile preparado para multiplataforma; evite dependências específicas de CPU sem alternativa.
  • Pipeline no GitHub Actions criado; veja como configurar pipeline de deploy automático.
  • Para imagens enxutas, aplique multistage; referência: como criar imagens Docker leves: multistage.

Estruture o Dockerfile para multiplataforma (TARGETARCH/TARGETOS)

BuildKit injeta variáveis de contexto como TARGETOS, TARGETARCH e BUILDPLATFORM. Use essas variáveis para compilar binários na arquitetura alvo e escolher bases corretas em estágios de build.

O exemplo abaixo compila um binário Go estático por arquitetura e publica uma imagem final mínima. Adapte os comandos ao seu projeto e linguagem. Se seu build usa bibliotecas nativas, verifique a compatibilidade com cross‑compile.

# Exemplo Dockerfile multiplataforma (Go)
# Estágio de build roda na plataforma do builder
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src

# Dependências de módulos
COPY go.mod go.sum ./
RUN go mod download

# Código da aplicação
COPY . .

# Compila binário para a arquitetura alvo
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
    go build -o /out/app ./cmd/app

# Imagem final mínima
FROM alpine:3.20
WORKDIR /app
COPY --from=build /out/app /usr/local/bin/app

# Labels OCI úteis para rastreabilidade
LABEL org.opencontainers.image.source="https://example.com/repo" \
      org.opencontainers.image.description="App multi-arch amd64/arm64"

USER 10001
ENTRYPOINT ["app"]

Workflow GitHub Actions: Buildx, QEMU, login e push multi‑arch

O workflow instala QEMU e Buildx, autentica no registro e usa o build‑push‑action para construir e publicar, em uma única etapa, as variantes amd64 e arm64. O cache do BuildKit reduz tempo de build entre execuções.

Ajuste as tags conforme sua estratégia. O metadata‑action ajuda a gerar tags por branch, tag de release e SHA automaticamente. Para GHCR, o GITHUB_TOKEN geralmente funciona para publicar pacotes do repositório.

name: build-and-push-multiarch

on:
  push:
    branches: ["main"]
    tags: ["v*", "v*.*.*"]
  pull_request:

jobs:
  docker:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Docker meta (tags e labels)
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ghcr.io/${{ github.repository }}
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=sha
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
          labels: |
            org.opencontainers.image.source=${{ github.repositoryUrl }}
            org.opencontainers.image.revision=${{ github.sha }}

      - name: Login GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # Exemplo opcional: login também no Docker Hub
      # - name: Login Docker Hub
      #   uses: docker/login-action@v3
      #   with:
      #     username: ${{ secrets.DOCKERHUB_USERNAME }}
      #     password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build & Push multi-arch
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Estratégia de tags e labels que evitam confusão

Defina convenções claras para cada evento do repositório. Use latest apenas para a linha estável principal. Em releases, publique tags imutáveis (semver) e, se fizer sentido, tags abreviadas para faixas.

Inclua labels OCI para rastreabilidade de origem e commit. Evite sobrescrever tags de versões antigas. Preserve a mesma lista de plataformas em todas as tags estáveis.

  • Branches principais: latest e SHA do commit.
  • Releases: vX.Y.Z, vX.Y, vX e latest (se representar a versão estável atual).
  • Pull requests: tags efêmeras (por exemplo, pr-<número>) sem promover a latest.
  • Inclua labels org.opencontainers.image.source e revision para auditoria.
Evento Tags recomendadas
Push na main latest, sha-<CURTO>
Tag v1.2.3 v1.2.3, v1.2, v1, latest (opcional)
Pull request pr-<número>, sha-<CURTO> (sem latest)

Publicar e inspecionar o manifesto gerado

Ao construir com múltiplas plataformas no Buildx e fazer push, o manifesto é criado automaticamente. Inspecione o resultado para conferir que amd64 e arm64 constam na mesma tag.

Use a ferramenta de inspeção para verificar plataformas, digests e camadas. Isso ajuda a diagnosticar pulls que falham por ausência de variante específica.

# Inspecione a manifest list publicada
# Ajuste a referência para seu registro e tag
docker buildx imagetools inspect ghcr.io/<owner>/<repo>:<tag>

# Opcional: inspecione um digest específico
# docker buildx imagetools inspect ghcr.io/<owner>/<repo>@sha256:<digest>

Como confirmar em cada arquitetura (amd64/arm64)

Valide a execução forçando a plataforma no cliente. Cada comando deve imprimir a arquitetura do kernel dentro do container. Assim, você garante que a imagem correta foi baixada a partir do mesmo tag.

Se o binário depender de bibliotecas nativas, execute um teste rápido de funcionalidade em cada plataforma para antecipar problemas de runtime.

# Força execução como amd64
docker run --rm --platform linux/amd64 ghcr.io/<owner>/<repo>:<tag> uname -m

# Força execução como arm64
docker run --rm --platform linux/arm64 ghcr.io/<owner>/<repo>:<tag> uname -m

Erros comuns, diagnósticos e otimizações de cache

Alguns problemas aparecem em builds multiplataforma. Abaixo, um guia rápido de diagnóstico e solução. Para reduzir o tempo de build, use cache remoto do BuildKit e baseie camadas em arquivos estáveis.

Para técnicas de cache avançadas no CI, confira o guia dedicado: como usar BuildKit e cache remoto no CI Docker.

  • no matching manifest: a tag não inclui a plataforma desejada.
  • exec format error: binário para a arquitetura errada foi copiado na imagem final.
  • Falha ao registrar QEMU: o runner não carregou binfmt; garanta setup‑qemu no workflow.
  • Cross‑compile com bibliotecas nativas: ajuste toolchains e flags para cada TARGETARCH.
  • Cache ineficiente: reorganize camadas e ative cache‑to/cache‑from do BuildKit.
Sintoma Causa provável Como resolver
no matching manifest para arm64 Tag publicada só com amd64 Garanta platforms: linux/amd64,linux/arm64 no build e republique
exec format error ao iniciar Binário amd64 copiado em imagem arm64 Use TARGETOS/TARGETARCH no build e COPY do artefato correto
Build lento em PRs Cache não compartilhado ou inválido Ative cache gha e fixe passos COPY para maximizar reaproveitamento
Erro ao rodar toolchain nativo no cross Dependências específicas da CPU Use compilações estáticas ou toolchains multiplataforma apropriadas

Perguntas frequentes

Preciso de runners arm64 para construir imagens arm64?

Na maioria dos casos, não. O Docker Buildx com QEMU permite construir variantes arm64 a partir de um runner amd64. Builds nativos podem ser mais rápidos e necessários para projetos com dependências de sistema específicas, mas para muitas aplicações o cross‑build funciona bem.

Buildx já publica o manifesto ou preciso criar manualmente?

Se você usa o build‑push‑action com múltiplas plataformas e push habilitado, o manifesto é criado automaticamente na tag informada. A criação manual só é necessária para compor manifestos usando imagens já existentes ou para retagging específico.

Como escolher entre Docker Hub e GHCR para publicar?

Ambos funcionam. Avalie permissões, integração com repositórios e controle de acesso. Você pode publicar em ambos, autenticando nos dois registros e usando duas referências de tag no mesmo build. Ajuste segredos e permissões conforme a política do seu time.

Como reduzir o tamanho da imagem final multi‑arch?

Use multistage para manter só o artefato necessário, escolha bases mínimas e elimine ferramentas de build no estágio final. Prefira binários estáticos quando possível e ordene COPY/RUN para maximizar cache. O mesmo Dockerfile atende amd64 e arm64 sem duplicação.

Posso ter tags diferentes por arquitetura?

Pode, mas é raro e aumenta a complexidade. O padrão é uma tag única apontando para um manifesto que referencia as variantes por plataforma. Use tags por arquitetura apenas quando há diferenças funcionais entre builds que exijam separação explícita.

QEMU é suficiente para projetos com dependências nativas (por exemplo, C/C++)?

Depende do projeto. QEMU cobre muitos casos, mas toolchains e bibliotecas nativas podem exigir configuração extra ou builds nativos. Avalie compilar estaticamente quando possível e mantenha testes por arquitetura para validar binários resultantes.

Pronto para publicar imagens multi‑arch com confiança?

Se você quer revisar seu Dockerfile e seu workflow de release multiplataforma no GitHub Actions, envie uma mensagem com o repositório e o objetivo das suas tags.

Quero revisar meu workflow

Veja também