Build e push de imagem Docker com GitHub Actions
Construir imagens Docker no CI e publicar em um registry é o padrão para deploys reproduzíveis — a VPS sempre executa exatamente a imagem que passou pelos testes. GitHub Container Registry (GHCR) é gratuito para repositórios públicos e privados com GitHub Free, eliminando a necessidade de Docker Hub ou registries pagos.
Workflow completo de build e push
Build da imagem Docker e push para GitHub Container Registry com cache:
# .github/workflows/docker-build.yml
name: Build e Push Docker
on:
push:
branches: [main, develop]
tags: ['v*.*.*']
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }} # ex: usuario/minha-api
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # necessário para push no GHCR
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login no GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} # automático, sem config
- name: Extrair metadata (tags e labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
- name: Configurar Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build e Push
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha # cache do GitHub Actions
cache-to: type=gha,mode=maxEstratégia de tags automáticas
O metadata-action gera tags automaticamente baseadas em branch, tag e commit:
# Tags geradas automaticamente pelo metadata-action:
# Push no branch main:
# ghcr.io/usuario/app:main
# ghcr.io/usuario/app:sha-abc1234
# Push de tag v1.2.3:
# ghcr.io/usuario/app:1.2.3
# ghcr.io/usuario/app:1.2
# ghcr.io/usuario/app:latest (tag semver com latest=true)
# Pull Request:
# Imagem buildada mas não publicada (push: false)
# Útil para verificar que o build não quebra
# Usar a imagem na VPS:
# docker compose pull
# docker compose up -d
# docker-compose.yml na VPS:
# services:
# app:
# image: ghcr.io/usuario/minha-api:main
# # ou: image: ghcr.io/usuario/minha-api:1.2.3Pull de imagem privada na VPS
Para fazer pull de imagem privada do GHCR na VPS, autentique o Docker:
# Na VPS — autenticar no GHCR:
# 1. Criar Personal Access Token no GitHub:
# Settings → Developer Settings → Personal access tokens (classic)
# Scopes: read:packages
# 2. Login na VPS:
echo "SEU_GITHUB_TOKEN" | docker login ghcr.io -u SEU_USUARIO --password-stdin
# 3. Verificar autenticação:
cat ~/.docker/config.json | grep ghcr.io
# 4. Docker Compose puxa automaticamente no deploy:
docker compose pull app
docker compose up -d --no-deps app
# Alternativa via GitHub Actions secret (mais seguro):
# No workflow, injete o token via SSH:
# ssh deploy@VPS "echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u ${{ github.actor }} --password-stdin"Multi-platform: build para ARM e AMD64
Para VPS ARM (Ampere, Graviton) ou Raspberry Pi, faça build multi-platform:
# Habilitar QEMU para emulação
- name: Configurar QEMU
uses: docker/setup-qemu-action@v3
- name: Configurar Buildx
uses: docker/setup-buildx-action@v3
- name: Build multi-platform
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
# O Docker seleciona automaticamente a imagem correta
# para a arquitetura da máquina que faz o pull:
# VPS AMD64 → pega a camada linux/amd64
# VPS ARM64 → pega a camada linux/arm64Verificar vulnerabilidades na imagem com Trivy
Escaneie a imagem por CVEs no próprio CI antes de fazer push:
- name: Scan de vulnerabilidades com Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
format: 'table'
exit-code: '1' # falhar o CI se encontrar CVE crítica
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
# Alternativa: gerar SBOM (Software Bill of Materials)
- name: Gerar SBOM
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
format: 'cyclonedx'
output: 'sbom.json'
- name: Upload SBOM como artefato
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.