Ambientes de preview em PRs com GitHub Actions e Docker Compose — passo a passo
Por John Pires · 19 de março de 2026

Ambientes de preview em PRs com GitHub Actions e Docker Compose — passo a passo
1) Por que aprender isso em 2026
Contexto atual e motivação
Em 2026, equipes distribuídas e integração contínua são padrão. Review de código não é só revisar diffs: é validar comportamento do sistema. Ambientes de preview (deploy efêmero por PR) encurtam o ciclo de feedback, aumentam a confiança em mudanças e reduzem bugs em produção. Para quem está começando, entender como montar um preview com Docker Compose e GitHub Actions entrega uma habilidade prática e repetível.
Analogia simples
Pense em um preview como uma cópia temporária do seu playground. Em vez de testar mudanças no mesmo parque onde as outras pessoas brincam, você cria uma caixa isolada e descarta depois. Docker Compose é a caixa, GitHub Actions é o encarregado que monta a caixa automaticamente quando você abre um PR.
Antes de partir para exemplos, dois pontos importantes: 1) previews devem ser rápidos e descartáveis; 2) não exponha dados sensíveis. Vamos trabalhar com serviços simples (API e banco em memória ou sqlite) para manter segurança e velocidade.
Regra prática: ambientes de preview servem para validação funcional e visual, não como ambiente de staging permanente.
2) Configuracao do ambiente
Ferramentas necessárias
Você precisa do Docker instalado (engine + CLI), docker-compose v2 ou superior (ou plugin compose), e uma conta no GitHub com repositório. Localmente, teste seus compose files; no CI, o runner do GitHub Actions já tem Docker disponível. Tenha também o Git configurado e, se precisar, acesso para criar secrets no repositório.
Estrutura mínima do repositório
Uma estrutura simples para começar:
- app/ - código da aplicação (ex: Node.js/Flask)
- docker/Dockerfile - imagem da app
- docker/docker-compose.yml - composição para desenvolvimento
- .github/workflows/preview.yml - workflow que cria o preview
Essa estrutura isola infra de código. O nome das pastas aqui é sugestão; o importante é manter consistência entre compose e workflow.
Segurança e secrets
Não commitse credenciais. No GitHub Actions, configure secrets via Settings > Secrets. Use variáveis de ambiente no workflow e injete apenas valores necessários. Para bancos de teste, prefira dados sintéticos ou bancos em memória.
3) Hello World progressivo
Primeiro erro: fazer preview com imagem pública e dados reais
Um erro comum de iniciantes é rodar em preview a mesma imagem usada em produção apontando para serviços reais. Isso pode causar efeitos colaterais e vazamento de dados. Expondo serviços internos ou utilizando credenciais reais, você transforma um preview em risco.
Jeito errado: workflow que chama deploy para produção
Exemplo de um erro prático em workflow: executar um script de deploy que usa credenciais de produção diretamente no PR. Veja o trecho problemático (não execute):
# Jeito errado: deploy direto para o ambiente de producao (exemplo nao funcional de objetivo didatico)
# Este arquivo demonstra o erro: ele chama um script de deploy com SECRET_PROD presente
name: preview-bad
on:
pull_request:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy-to-production.sh
O que faz: chama um deploy que pode usar variáveis de produção. O que acontece: quando aberto um PR, alterações acidentais podem ser promovidas, e revisores não conseguem isolar impacto.
Jeito certo: criar preview isolado com Docker Compose
Melhor abordagem: construir imagem da branch e orquestrar serviços via Docker Compose apenas no runner, usando variáveis seguras e portas dinâmicas. Workflow cria um compose file temporário e expõe um endpoint de verificação, sem tocar produção.
Abaixo um Dockerfile simples para uma API Node.js (exemplo real funcional básico). Contexto: builda a imagem usada no preview. Depois do bloco eu explico o que ocorre.
# Dockerfile: construi uma imagem minima para a app de exemplo
FROM node:18-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
O que faz: cria uma imagem leve baseada em node:18-alpine, instala dependências e define comando de execução. O que acontece ao buildar: a imagem está pronta para rodar em um container que serve a API na porta 3000.
Compose de preview: jeito certo e isolado
Exemplo de docker-compose específico para preview. Contexto: ele monta a app e um banco sqlite em volume temporário. Depois explico comportamento.
# docker/docker-compose.preview.yml
version: '3.8'
services:
app:
build:
context: ..
dockerfile: docker/Dockerfile
ports:
- "0:3000" # porta dinamica no host do runner
environment:
- NODE_ENV=preview
- DATABASE_URL=sqlite:/data/db.sqlite
volumes:
- preview_data:/data
volumes:
preview_data:
driver: local
O que faz: define serviço app com build a partir da branch atual e monta um volume temporário. A porta host usa '0:3000' que no runner permite mapear porta aleatória. O que acontece: ao subir containers, runner atribui uma porta de host e sua aplicação fica acessível localmente no runner, usada apenas para checks automáticos no workflow.
4) Mini-projeto pratico
Objetivo do mini-projeto
Vamos montar um fluxo mínimo que cria um ambiente de preview para cada Pull Request, executa testes de smoke (curl para health) e registra logs. Tudo isso usando apenas GitHub Actions + Docker Compose, sem necessidade de cloud externo.
Workflow GitHub Actions: explicação e arquivo
A seguir, o workflow que faz checkout, constrói imagem, executa docker compose, descobre a porta e faz uma verificação. Contexto: esse arquivo roda apenas em PRs e não empurra nem modifica ambientes externos.
# .github/workflows/preview.yml
name: preview
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Build image for preview
run: |
docker build -f docker/Dockerfile -t preview:${{ github.sha }} .
- name: Run preview compose
run: |
mkdir -p /tmp/preview-${{ github.run_id }}
cp docker/docker-compose.preview.yml /tmp/preview-${{ github.run_id }}/compose.yml
cd /tmp/preview-${{ github.run_id }}
docker compose -f compose.yml up -d
- name: Find mapped port
id: port
run: |
CONTAINER_ID=$(docker ps --filter "ancestor=preview:${{ github.sha }}" --format "{{.ID}}" | head -n1)
PORT=$(docker port $CONTAINER_ID 3000/tcp | sed 's/.*://')
echo "port=$PORT" >> $GITHUB_OUTPUT
- name: Smoke test health
run: |
curl -sSf "http://localhost:${{ steps.port.outputs.port }}/health" | jq .
- name: Tear down
if: always()
run: |
cd /tmp/preview-${{ github.run_id }}
docker compose -f compose.yml down -v
O que faz: este workflow builda a imagem, sobe o compose em background, detecta a porta exposta, faz um curl para /health e derruba containers. O que acontece: cada PR executa esse pipeline isolado no runner; se o health falhar, o job falhará e o revisor verá erro no PR.
Script de wait e fallback (exemplo real funcional)
Em ambientes reais, o serviço pode demorar para subir. Um script de espera robusto evita falsos negativos. Contexto: crie scripts/wait-for-health.sh e use no workflow antes do curl.
# scripts/wait-for-health.sh
#!/usr/bin/env bash
set -euo pipefail
base_url="$1"
retries=${2:-10}
interval=${3:-2}
for i in $(seq 1 $retries); do
if curl -sSf "$base_url/health" >/dev/null 2>&1; then
echo "ok"
exit 0
fi
echo "waiting... ($i/$retries)"
sleep $interval
done
echo "service did not become healthy" >&2
exit 1
O que faz: tenta acessar /health até o limite de retries. O que acontece: evita falhas imediatas se a app demorar para inicializar; o workflow deve chamar esse script antes de executar testes mais complexos.
Erros comuns e armadilhas
Listo aqui problemas frequentes e soluções práticas. Esses itens são baseados em code reviews feitos em projetos reais.
- Expor portas fixas: usar portas fixas (3000:3000) causa colisão se jobs paralelos rodarem. Use porta dinâmica ou namespace por run.
- Credenciais no repositório: nunca commitse arquivos com secrets. Use GitHub Secrets e variáveis de ambiente no workflow.
- Volumes persistentes entre runs: limpar volumes com docker compose down -v para evitar dados antigos interferindo.
- Logs insuficientes: capture logs do container (docker logs) no passo de Tear down para depuração quando algo falhar.
Dica: sempre inclua passo final que coleta docker logs e artifacts (se aplicável). Logs salvam code reviews longos e evitam reproduzir o erro localmente.
5) Recursos para continuar estudando
Leitura e documentação
Documentação oficial é ponto de partida: Docker Docs, Docker Compose reference e GitHub Actions docs. Além disso, artigos sobre runner self-hosted e ambientes efêmeros ampliam horizontes. Procure por exemplos de preview environments em frameworks que você usa (Next.js, Rails, Django).
Ferramentas complementares
Ferramentas que podem facilitar previews em equipes: ngrok (túnel), external-dns + ingress controllers (para previews em k8s), e plataformas como Okteto ou StackBlitz para previews mais integrados. Cada uma tem trade-offs: custo, complexidade, segurança.
Para projetos pequenos e aprendizado, Docker Compose + GitHub Actions é o caminho mais simples. Você entende cada passo e não depende de terceiros.
Conclusão prática
Resumo direto
Workflow eficiente para previews: construir a imagem da branch, subir containers isolados com docker compose, executar checks automatizados e derrubar recursos. Evite deploys diretos para produção, não exponha secrets e automatize coleta de logs em falhas.
Checklist para aplicar agora
- Adicionar Dockerfile que construa sua aplicação de forma reprodutível.
- Criar docker-compose.preview.yml com porta dinâmica e volumes temporários.
- Implementar workflow GitHub Actions que builda, sobe compose, espera health e derruba recursos.
- Configurar secrets no GitHub e evitar qualquer credencial em commits.
- Adicionar coleta de logs e artifacts para facilitar code review.
Próximo passo prático: copie os arquivos mostrados, adapte caminhos e nomes, e abra um PR para ver o preview rodando no runner. Se algo falhar, confira logs e revise as portas e volumes.
Este artigo demonstrou o jeito errado (deploy para produção em PR) e o jeito certo (preview efêmero e isolado). Aplique o padrão, automatize e mantenha os previews rápidos e seguros.
Se ficou com dúvidas sobre scripts específicos do seu framework (Rails, Django, Node), adapte os comandos de build e health; o fluxo geral se mantém. Bons previews aceleram reviews e reduzem surpresas em produção.
Obrigado por levar isso até o fim: aprender a automatizar previews é um ganho real em produtividade e qualidade de código.
Assuntos: DevOps, github-actions, docker-compose, ci-cd, preview-environment
Leia também
Canary deploy com GitHub Actions e Docker: métricas, rollout e rollback automático
Pipeline prático de canary deploy com GitHub Actions, Docker e Kubernetes: exemplos funcionais de Dockerfile, manifests Nginx-canary, workflow e script de…
Canary deploy com GitHub Actions em Kubernetes usando Helm e Flagger
Guia prático para implementar canary deploy com GitHub Actions, Helm e Flagger: padrões de pipeline, exemplos de Canary CRD e AnalysisTemplate, integrações…
Docker Buildx + GitHub Actions: publicar imagens multi-arch (ARM/x86) corretamente
Guia prático para publicar imagens Docker multi-arch (arm64/x86) usando Docker Buildx e GitHub Actions. Mostro o jeito errado, o certo, workflows, debug e…
Escanear imagens Docker no CI com Trivy e GitHub Actions — falhe por CVSS
Aprenda a escanear imagens Docker no CI com Trivy e GitHub Actions e a fazer o pipeline falhar quando uma vulnerabilidade atingir um limiar CVSS. Passo a…
GitHub Actions: build, testar em container e publicar imagem no GHCR
Pipeline prática: build de imagem Docker, executar testes no container carregado e publicar no GHCR usando GitHub Actions. Código e workflow para iniciar e…
Testcontainers e GitHub Actions: testar imagens Docker na CI corretamente
Guia prático de como rodar testes de integração com imagens Docker usando Testcontainers no GitHub Actions: padrões certos e anti-patterns, workflows…