Destino Dev

Blog

Docker Buildx + GitHub Actions: publicar imagens multi-arch (ARM/x86) corretamente

Por John Pires · 11 de março de 2026

Docker Buildx + GitHub Actions: publicar imagens multi-arch (ARM/x86) corretamente

Introdução ao problema

O que é multi-arch e por que importa

Arquiteturas de processador diferentes (x86_64, arm64) exigem binários diferentes. Hoje, servidores cloud, Raspberry Pis e laptops Apple Silicon executam arquiteturas distintas. Se a sua imagem Docker só for construída para amd64, ela vai falhar em máquinas arm64. O objetivo do artigo é eliminar surpresas: publicar uma imagem que funcione tanto em ARM quanto em x86 sem deploy manual em várias arquiteturas.

Analogia simples para entender o problema

Imagine empacotar um aplicativo web dentro de uma caixa que contém dependências compiladas para uma única língua. Se um cliente fala outra língua, seu produto não roda. Multiplataforma é empacotar traduções dentro da mesma caixa: o cliente escolhe a tradução adequada automaticamente. No Docker, esta caixa é o repositório de imagens que contém referências para imagens específicas por arquitetura.

Primeira armadilha: muitos começam empacotando e publicando imagens apenas da arquitetura local. Isso funciona no seu laptop, mas falha no servidor. No fluxo a seguir, vou mostrar o jeito errado — o mais comum — e como corrigir com buildx e GitHub Actions.

Dica: sempre teste sua imagem em pelo menos duas arquiteturas antes de confiar no CI. É barato — e evita incidentes ao rodar em hardware real.

Pré-requisitos e setup

Requisitos mínimos de ambiente

Você precisa de:

  • Conta no Docker Hub ou GitHub Container Registry (GHCR)
  • Repositório no GitHub onde o workflow será executado
  • Familiaridade básica com Dockerfile e GitHub Actions
  • Docker CLI atualizada localmente (para testes) e docker-buildx plugin, embora o Actions faça isso para você

Configuração de credenciais no GitHub

Crie segredos no repositório GitHub: CR_PAT (Docker Hub token) ou GHCR_TOKEN dependendo do registry usado. No caso do Docker Hub, gere um Personal Access Token com permissões de push. No GHCR, use o PAT com write:packages e read:packages.

Erros comuns nesta etapa:

  1. Usar senha normal em vez de token — logout automático por rate limits.
  2. Permissões insuficientes — push falha com 403.

Tutorial guiado passo a passo

O jeito errado (code review): Dockerfile e build local apenas amd64

Exemplo de Dockerfile que muitos iniciantes usam: base genérica, copia binário compilado localmente e publica. Isso quebra em arm64 porque o binário foi compilado para amd64.

Antes do código: este Dockerfile assume que o binário do aplicativo foi compilado na máquina local e copiado para a imagem. Em CI sem build cross-compile, o binário resultante não funciona em outra arquitetura.

FROM alpine:3.18
WORKDIR /app
COPY myapp /app/myapp
EXPOSE 8080
CMD ["/app/myapp"]

Após: se você buildar em uma máquina x86 e enviar essa imagem, ela conterá um binário x86. Em arm64, a imagem vai falhar com "exec format error". É um erro clássico.

O jeito certo: multi-stage + cross-compilation ou imagens multi-arch

Correção: compilar o binário dentro da imagem usando uma etapa de build que rode em arquituras diferentes ou usar imagens base que suportem multi-arch. Usaremos uma imagem base que contém ferramentas de build e compilaremos para a arquitetura alvo com a variável GOARCH ou com buildx.

Exemplo de Dockerfile para uma aplicação Go (DevOps real: serviço que expõe /metrics): compila dentro do container para a arquitetura do builder.

FROM golang:1.20-alpine AS builder
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /out/myservice ./cmd/myservice

FROM scratch
COPY --from=builder /out/myservice /myservice
EXPOSE 8080
ENTRYPOINT ["/myservice"]

O que acontece: este Dockerfile gera um binário estático dentro do estágio builder. Quando você usa buildx para compilar para múltiplas plataformas, o passo de build é executado em cada plataforma target, produzindo binários corretos por arquitetura.

Workflow GitHub Actions: configurar buildx e publicar multi-arch

Agora o fluxo do GitHub Actions. O arquivo abaixo configura o builder docker/build-push-action e publica imagens multi-arch no Docker Hub (ou GHCR). Antes do código: substitua ${{ secrets.DOCKERHUB_USERNAME }} e ${{ secrets.DOCKERHUB_TOKEN }} pelos seus segredos.

name: CI - multi-arch build and push
on:
  push:
    branches: [ main ]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

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

      - name: Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push multi-arch image
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: yourusername/yourrepo:latest

Após: o action executa QEMU para emular arquiteturas, cria imagens para amd64 e arm64 e publica um manifest que aponta para cada imagem. Consumidores docker pull yourusername/yourrepo:latest recebem a versão correta automaticamente.

Resultado final funcionando

Como validar localmente (inspeção e pull)

Valide com docker manifest inspect ou docker buildx imagetools inspect. Antes de rodar os comandos, substitua o nome da imagem pelo nome público que você usou.

# Inspeciona manifest multi-arch
docker buildx imagetools inspect yourusername/yourrepo:latest

# No cliente docker, teste o pull e rode uma arquitetura específica (se suportado)
docker run --rm --platform linux/arm64 yourusername/yourrepo:latest /myservice --version

O que acontece: o imagetools mostrará várias entradas de plataforma. Se tudo estiver certo, haverá imagens para linux/amd64 e linux/arm64.

Debug comum: "exec format error" e como resolver

Se você receber exec format error ao executar a imagem em arm64, verifique duas coisas:

  • Se o build realmente gerou binários para arm64 (o buildx precisa estar configurado corretamente)
  • Se a stage de build produz binários estáticos ou correto para a arquitetura
Dica: use logs do action e o inspect do imagetools para confirmar que cada plataforma foi construída.

Erros comuns, armadilhas e dicas práticas

Erro: compilar localmente e copiar binário

Muitos projetos copiam um binário compilado localmente para a imagem. Isso quebra multi-arch. Sempre prefira compilar dentro do Dockerfile (multi-stage) ou usar imagens base multi-arch.

Erro: esquecer setup-qemu e buildx

Sem QEMU e buildx, o GitHub Actions tentará construir apenas para a arquitetura do runner (amd64). Resultado: imagem só amd64. O passo docker/setup-qemu-action e docker/setup-buildx-action é obrigatório para builds multi-arch em runners x86.

Outras dicas práticas:

  • Use tags sem latest em produção; adote tags semânticas (v1.2.3).
  • Testes automatizados: execute containers em emulação com qemu para validar comportamento antes do push.
  • Para imagens grandes, otimize camadas para reduzir tempo e custo do runner.

Desafio para o leitor praticar

Exercício proposto

Meta: implementar suporte multi-arch para um serviço pequeno (por exemplo, um exporter Prometheus em Go ou um microservice Express com binário nativo). Faça um PR com workflow GitHub Actions que produz imagens para amd64 e arm64 e publique no seu registry.

Checklist de validação

  1. Dockerfile usa multi-stage e compila dentro do container
  2. Workflow configura setup-qemu e setup-buildx
  3. Imagens publicadas têm manifest multi-arch (verificado com imagetools inspect)
  4. Container roda em uma máquina ARM (pode usar Raspberry Pi ou runner com emulação)

Se quiser, crie dois workflows: um para PR (build + tests sem push) e outro para main (build multi-arch + push). Isso evita push de imagens incompletas em branches de feature.

Conclusão prática

Resumo dos passos essenciais

Os pontos essenciais para publicar imagens multi-arch são claros: compilar dentro do Dockerfile (multi-stage), configurar QEMU e Buildx no CI, e usar docker/build-push-action com a lista de plataformas desejadas. Corrigir o erro comum de copiar binário local resolve a maioria dos problemas.

Próximos passos recomendados

Depois de publicar multi-arch, monitore imagem e uso com scanners de segurança, faça builds reproducíveis e reduza camadas. Integre testes que executem containers em ambas as arquiteturas para garantir comportamento idêntico.

Este guia mostrou o caminho errado (compilar local + publicar) e o caminho certo (buildx + QEMU + workflow). O próximo passo é aplicar no seu repositório: crie o Dockerfile multi-stage, configure segredos no GitHub e copie o workflow do tutorial. Valide com docker buildx imagetools inspect e rode uma instância em ARM para confirmar.

Boa prática: trate multi-arch como requisito de qualidade, não como luxo. Projetos que ignoram arquitetura criam incidentes sutis em infra heterogênea.

Erro comum final que vale repetir: se um runner local (amd64) buildar e você subir sem buildx, a imagem parecerá funcionar pra você, mas não para metade do parque de execução. Evite esse falso positivo.

Pronto. Faça o exercício, abra PR com o workflow e valide os manifests. Se algo quebrar, revise logs do action: eles mostram claramente se o build para cada plataforma foi bem-sucedido.

Assuntos: Docker, github-actions, buildx, multi-arch, ci-cd, DevOps

Leia também