Docker Compose para times pequenos: do ambiente local ao staging

1. Por que Docker Compose é a escolha certa para times pequenos

Times pequenos precisam de ferramentas que entreguem valor imediato sem exigir uma equipe dedicada de infraestrutura. Docker Compose preenche exatamente esse espaço: oferece orquestração de múltiplos containers com um arquivo YAML simples, sem a complexidade operacional do Kubernetes.

Quando um time tem de 2 a 10 desenvolvedores, manter um cluster Kubernetes é desproporcional. Docker Compose permite que todos os serviços — aplicação web, banco de dados, cache, filas — rodem localmente com um único comando. A curva de aprendizado é baixa: desenvolvedores entendem o básico em uma tarde e focam no que realmente importa, o código do produto.

O custo financeiro também é zero para o ambiente local e mínimo para staging. Uma VPS de US$ 10/mês na Hetzner ou DigitalOcean roda perfeitamente um stack completo com Docker Compose.

2. Estrutura de projeto: organizando o docker-compose.yml para múltiplos ambientes

A chave para usar Docker Compose em múltiplos ambientes é separar configurações comuns das específicas. Crie um arquivo base e use sobreposições.

# docker-compose.yml (base - compartilhado entre ambientes)
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
      - redis
    env_file:
      - .env

  db:
    image: postgres:15-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: app_user

  redis:
    image: redis:7-alpine

volumes:
  pgdata:

Para o ambiente local, crie um override que adiciona montagens de volume para hot reload:

# docker-compose.override.yml (automático no ambiente local)
services:
  app:
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development

Para staging, use um arquivo separado:

# docker-compose.staging.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.prod
    restart: always
    environment:
      - NODE_ENV=production

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf

Execute com:

docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d

3. Ambiente local: produtividade e recriação rápida

No desenvolvimento local, a capacidade de recriar o ambiente rapidamente é essencial. Use volumes bind mount para que as alterações no código sejam refletidas instantaneamente no container.

Para Node.js com hot reload:

services:
  app:
    build: .
    volumes:
      - .:/app
      - /app/node_modules
    command: npm run dev

Para inicializar dados de teste, crie um script SQL executado na primeira inicialização do banco:

# init.sql
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(100) UNIQUE
);

INSERT INTO users (name, email) VALUES ('Admin', 'admin@example.com');

Monte-o no container PostgreSQL:

services:
  db:
    image: postgres:15-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

4. Staging: do laptop para um servidor de baixo custo

O deploy em staging pode ser tão simples quanto copiar os arquivos para uma VPS e executar Docker Compose. Use perfis para ativar serviços opcionais como monitoramento ou workers.

# docker-compose.yml com profiles
services:
  app:
    build: .
    ports:
      - "3000:3000"

  worker:
    build: .
    command: npm run worker
    profiles:
      - staging

  monitoring:
    image: portainer/portainer-ce
    ports:
      - "9000:9000"
    profiles:
      - monitoring

No servidor staging:

docker compose --profile staging up -d

Configure reinicialização automática e logs:

services:
  app:
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

5. Rede e comunicação entre serviços: sem complicação

Docker Compose cria automaticamente uma rede para todos os serviços, permitindo comunicação pelo nome do serviço. Use isso para conectar a aplicação ao banco e cache.

# No .env
DATABASE_URL=postgres://app_user:password@db:5432/myapp
REDIS_URL=redis://redis:6379

Para expor a aplicação com Nginx como reverse proxy:

# nginx.conf
server {
    listen 80;
    server_name staging.myapp.com;

    location / {
        proxy_pass http://app:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /api/ {
        proxy_pass http://app:3000;
        proxy_set_header Host $host;
    }
}

6. Dados persistentes: volumes e backup para times pequenos

Use volumes nomeados para dados que precisam persistir entre deploys:

volumes:
  pgdata:
  redis-data:

Para backup simples do banco PostgreSQL:

# backup.sh
docker compose exec -T db pg_dump -U app_user myapp > backup_$(date +%Y%m%d).sql

Execute via cron no host:

0 3 * * * cd /opt/myapp && ./backup.sh

Para migrações de banco, execute dentro do pipeline CI:

docker compose run --rm app npm run migrate

7. CI/CD leve: integrando Docker Compose ao fluxo de trabalho

No pipeline CI, use Docker Compose para rodar testes em ambiente idêntico ao de produção:

# .github/workflows/test.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: |
          docker compose up -d db redis
          docker compose run --rm app npm test

Para deploy contínuo no staging:

deploy-staging:
  runs-on: ubuntu-latest
  steps:
    - name: Deploy via SSH
      uses: appleboy/ssh-action@v1.0.0
      with:
        host: ${{ secrets.STAGING_HOST }}
        username: ${{ secrets.STAGING_USER }}
        key: ${{ secrets.STAGING_KEY }}
        script: |
          cd /opt/myapp
          git pull origin main
          docker compose pull
          docker compose up -d

8. Limitações e quando migrar para algo maior

Docker Compose resolve bem problemas de times pequenos, mas tem limitações claras:

  • Escalabilidade horizontal: não há balanceamento de carga nativo entre múltiplas instâncias
  • Monitoramento avançado: docker stats e Portainer são suficientes para times pequenos, mas não para produção crítica
  • Alta disponibilidade: sem failover automático para serviços

Sinais de que é hora de migrar para Kubernetes ou Nomad:

  • Múltiplos serviços com requisitos de escalonamento independentes
  • Equipes separadas gerenciando diferentes partes do sistema
  • Necessidade de canary deployments e rollbacks automatizados
  • SLA acima de 99,9% exigindo redundância geográfica

Até lá, Docker Compose oferece o equilíbrio perfeito entre simplicidade e funcionalidade para times que querem entregar software sem se afogar em infraestrutura.

Referências