Deploy automatizado: tutorial passo a passo com GitHub Actions e Docker

21/10/2026  ·  Devskin

Deploy automatizado: tutorial passo a passo com GitHub Actions e Docker

Um deploy automatizado bem desenhado é o que separa times que publicam com segurança de times que dependem de acesso SSH manual e de torcida a cada release. Neste tutorial, mostro um fluxo enxuto e reproduzível: build da imagem, push para um registry, atualização no servidor via Docker Compose, verificação de saúde e rollback. É um padrão que atende bem SaaS pequenos e médios, sem exigir Kubernetes desde o primeiro dia.

Pré-requisitos e arquitetura do fluxo

O fluxo tem quatro peças: repositório Git, runner de CI, registry de imagens e um servidor (VM em qualquer cloud) com Docker e Docker Compose. A lógica é simples: um push na branch main dispara o pipeline, que gera uma imagem tagueada com o SHA do commit e aciona um script remoto que troca a versão em execução.

  • Repositório com Dockerfile funcional e endpoint /health na aplicação.
  • Registry: GitHub Container Registry (ghcr.io), Docker Hub ou um registry privado.
  • Servidor Linux com Docker Engine e plugin Compose instalados.
  • Usuário de deploy dedicado, com chave SSH própria e permissão apenas para o necessário.

Se você ainda não padronizou a execução dos containers em produção, vale ler antes o Docker Compose em Produção: Tutorial Passo a Passo para Times de TI, que cobre volumes, secrets e backup.

Passo 1: tagueie imagens de forma imutável

O erro mais comum em um deploy automatizado é usar apenas a tag latest. Ela é mutável, dificulta auditoria e torna o rollback impreciso. Use o SHA do commit como tag: cada imagem aponta para exatamente um estado do código, e voltar versão passa a ser trocar uma variável.

No docker-compose.yml do servidor, referencie a tag por variável de ambiente:

services:
  app:
    image: ghcr.io/sua-org/app:${APP_TAG}
    restart: unless-stopped
    ports:
      - '127.0.0.1:8080:8080'
    env_file: .env

Passo 2: configure o pipeline no GitHub Actions

Crie o arquivo .github/workflows/deploy.yml. O job faz checkout, autentica no registry, constrói e publica a imagem e, por fim, executa o script remoto por SSH. Confira sempre a documentação oficial de cada action e fixe versões para evitar mudanças inesperadas no pipeline.

name: deploy
on:
  push:
    branches: [main]
permissions:
  contents: read
  packages: write
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/sua-org/app:${{ github.sha }}
      - uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: /opt/app/deploy.sh ${{ github.sha }}

Cadastre SSH_HOST, SSH_USER e SSH_KEY em Settings > Secrets and variables > Actions. Nunca versione chaves ou arquivos .env no repositório.

Passo 3: escreva o script de deploy com healthcheck e rollback

A parte que dá confiabilidade ao deploy automatizado fica no servidor. O script registra a tag em execução, sobe a nova versão, testa o endpoint de saúde e, se falhar, restaura a tag anterior.

#!/usr/bin/env bash
set -euo pipefail
cd /opt/app
NEW=$1
OLD=$(cat .current_tag 2>/dev/null || echo none)

export APP_TAG=$NEW
docker compose pull app
docker compose up -d app

for i in $(seq 1 20); do
  if curl -fsS http://127.0.0.1:8080/health >/dev/null; then
    echo $NEW > .current_tag
    exit 0
  fi
  sleep 3
done

echo 'Healthcheck falhou, executando rollback' >&2
if [ $OLD != none ]; then
  export APP_TAG=$OLD
  docker compose up -d app
fi
exit 1

Dê permissão com chmod +x /opt/app/deploy.sh. Como o script sai com código 1 em caso de falha, o job do GitHub Actions fica vermelho e o time é notificado, enquanto o serviço continua na versão anterior.

Autenticação do servidor no registry

Para o docker compose pull funcionar com imagens privadas, faça docker login ghcr.io uma vez no servidor, usando um token com escopo apenas de leitura de pacotes. Não reaproveite o token de escrita do pipeline.

Trade-offs e limites desse modelo

Esse desenho é simples e barato, mas tem limites reais que você precisa conhecer antes de adotá-lo:

  • Downtime curto: o up -d recria o container, e pode haver uma janela de indisponibilidade. Para zero downtime, avalie um proxy reverso com dois containers (blue/green) ou um orquestrador.
  • Migrações de banco: rollback de imagem não desfaz alterações de schema. Adote migrações compatíveis com a versão anterior (expand/contract).
  • Servidor único: sem alta disponibilidade. Se o host cair, o serviço cai.
  • Acesso SSH pelo CI: exige proteção da chave, usuário restrito e, idealmente, firewall limitando origens.

Para quem precisa de mais escala, o mesmo pipeline pode evoluir para Kubernetes ou outro orquestrador, mantendo a imagem imutável como contrato entre build e execução.

Boas práticas para evoluir o pipeline

  • Adicione testes antes do build e bloqueie o deploy se falharem.
  • Use environments do GitHub com aprovação manual para produção.
  • Escaneie imagens com uma ferramenta de vulnerabilidades no próprio pipeline.
  • Centralize logs e alertas para correlacionar cada release a variações de erro e latência.
  • Revise custos da infraestrutura que hospeda o fluxo; o post Custo de Cloud Estrangeiro: o que eu mudaria hoje para cortar até 70% mostra por onde começar.

Teste o rollback de propósito, publicando uma versão com /health quebrado em ambiente de homologação. Um deploy automatizado só é confiável quando o caminho de falha foi exercitado, não apenas o caminho feliz.

Conclusão

Com imagens imutáveis, pipeline em GitHub Actions e um script com healthcheck e rollback, você ganha um deploy automatizado auditável, repetível e barato de operar. Comece pequeno, meça o tempo de cada release e evolua para blue/green ou orquestração quando o produto exigir. O ganho real está em tornar publicar algo rotineiro e seguro, liberando o time para entregar valor em vez de apagar incêndio.

Contact

Send a message

Get in touch