Um deploy automatizado elimina a etapa manual mais frágil do ciclo de entrega: o acesso ao servidor por SSH para copiar arquivos, reiniciar serviços e torcer para que nada quebre. Neste tutorial, você vai montar um fluxo completo com GitHub Actions, Docker e um servidor Linux, com health check e rollback básico. É uma base enxuta, ideal para times de TI que ainda não precisam de Kubernetes, mas já não aceitam deploy manual.
Arquitetura do deploy automatizado
Antes de escrever qualquer configuração, é importante entender o fluxo. A cada push na branch main, o pipeline executa estas etapas:
- Build: o GitHub Actions constrói a imagem Docker a partir do Dockerfile do repositório.
- Publicação: a imagem vai para um registry (aqui, o GitHub Container Registry), com o SHA do commit como tag.
- Release: o runner acessa o servidor via SSH e executa um script que baixa a nova imagem e troca o contêiner.
- Verificação: o script consulta um endpoint de saúde e, se falhar, restaura a versão anterior.
Usar o SHA do commit como tag, e não latest, garante rastreabilidade: você sempre sabe qual código está em produção e consegue reverter para uma versão exata.
Passo 1: Dockerfile e endpoint de saúde
O primeiro requisito de qualquer deploy automatizado confiável é uma aplicação que informe se está saudável. Exponha uma rota como /health que retorne HTTP 200 quando a aplicação estiver pronta, validando dependências críticas como o banco de dados. Um Dockerfile simples para uma aplicação Node.js seria:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]Ajuste a imagem base e o comando de inicialização para a sua stack. O princípio vale para qualquer linguagem: dependências em camadas separadas aproveitam o cache de build e reduzem o tempo do pipeline.
Passo 2: Preparar o servidor e a chave SSH
No servidor Linux, instale o Docker e crie um usuário dedicado, sem privilégios de root:
- Crie o usuário
deploye adicione-o ao grupodocker. - Gere um par de chaves ed25519 exclusivo para o pipeline e adicione a chave pública em
~/.ssh/authorized_keys. - Faça
docker login ghcr.iono servidor com um token com permissão apenas de leitura de pacotes. - Armazene a chave privada, o host e o usuário como secrets do repositório (
SSH_KEYeHOST).
Atenção: membros do grupo docker têm, na prática, poder equivalente ao root na máquina. Por isso, a chave do pipeline deve ser exclusiva e fácil de revogar.
Passo 3: Script de release com rollback
Salve este script em /opt/app/deploy.sh no servidor. Ele recebe a imagem completa como argumento, sobe o novo contêiner e valida a saúde:
#!/usr/bin/env bash
set -euo pipefail
IMAGE="$1"
PREV=$(docker inspect --format '{{.Config.Image}}' app 2>/dev/null || true)
docker pull "$IMAGE"
docker rm -f app 2>/dev/null || true
docker run -d --name app -p 3000:3000 --restart unless-stopped "$IMAGE"
for i in $(seq 1 15); do
if curl -fsS http://localhost:3000/health; then exit 0; fi
sleep 2
done
echo 'Health check falhou' >&2
docker rm -f app
if [ -n "$PREV" ]; then
docker run -d --name app -p 3000:3000 --restart unless-stopped "$PREV"
fi
exit 1O script guarda a imagem anterior antes de remover o contêiner. Se o health check não responder com sucesso após cerca de 30 segundos, ele restaura a versão anterior e retorna erro, fazendo o job do GitHub Actions falhar e sinalizando o problema ao time.
Passo 4: Workflow do GitHub Actions
Crie o arquivo .github/workflows/deploy.yml. Este é o núcleo do deploy automatizado:
name: deploy
on:
push:
branches: [main]
permissions:
contents: read
packages: write
jobs:
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@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Deploy via SSH
run: |
mkdir -p ~/.ssh
echo '${{ secrets.SSH_KEY }}' > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ secrets.HOST }} >> ~/.ssh/known_hosts
ssh deploy@${{ secrets.HOST }} '/opt/app/deploy.sh ghcr.io/${{ github.repository }}:${{ github.sha }}'Dois cuidados práticos: o nome do repositório no GHCR precisa estar em minúsculas, e vale conferir as versões atuais das actions na documentação oficial antes de adotar o arquivo em produção. Para maior segurança, registre a fingerprint do host como secret em vez de depender apenas do ssh-keyscan.
Trade-offs e quando evoluir
Esse modelo é simples e barato, mas tem limites claros:
- Downtime curto: como o contêiner é substituído, há uma breve janela de indisponibilidade. Blue/green com proxy reverso (Nginx ou Traefik) resolve isso.
- Servidor único: não há alta disponibilidade. Para múltiplos nós, considere orquestração com Kubernetes ou Nomad.
- Estado fora do pipeline: migrações de banco precisam de estratégia própria, de preferência compatíveis com versões consecutivas da aplicação.
- Infraestrutura manual: o servidor foi preparado à mão. Padronize-o depois, como mostra o review técnico do Terraform para times de TI.
Vale também observar que o mesmo raciocínio se aplica a tarefas repetitivas fora do código. Se você quer expandir a automação para outras rotinas da empresa, leia sobre como automatizar processos internos com IA sem depender de agência.
Conclusão
Com um Dockerfile, um registry, um script de release e um workflow de poucas linhas, você já tem um deploy automatizado rastreável, repetível e com rollback básico. Comece por esse modelo, meça o tempo de entrega e a taxa de falhas no seu contexto e só então avance para estratégias mais sofisticadas, como blue/green ou orquestração. Automatizar cedo é o que libera o time de TI para focar no que gera valor de verdade.