Docker Compose em produção: configuração correta

Para produção, um docker-compose.yml precisa de: restart: always (para subir automaticamente após reinicialização do servidor), healthchecks (para depends_on funcionar de verdade), logging com rotação (para não encher o disco), limites de recursos (para isolar falhas) e variáveis sensíveis em arquivo .env separado, nunca hard-coded no Compose.

Template base para produção

Estrutura mínima de um serviço de produção com todas as configurações essenciais:

yaml
services:
  app:
    image: minha-app:1.2.3          # versão fixa, nunca :latest em prod
    restart: always
    env_file: .env                  # variáveis sensíveis fora do compose
    ports:
      - "127.0.0.1:3000:3000"       # bind apenas no localhost, NGINX expõe externamente
    depends_on:
      db:
        condition: service_healthy  # aguarda o banco estar pronto
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

  db:
    image: postgres:16
    restart: always
    env_file: .env
    volumes:
      - pg_data:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          memory: 1G
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pg_data:

restart: always vs unless-stopped

Escolha a política de restart correta para cada tipo de serviço:

Arquivo .env e segredos

Variáveis sensíveis (senhas, API keys) nunca devem aparecer no docker-compose.yml, use arquivo .env:

bash
# .env (nunca commite no git — adicione ao .gitignore)
POSTGRES_USER=appuser
POSTGRES_PASSWORD=senha_super_segura_aqui
POSTGRES_DB=producao
SECRET_KEY=chave_jwt_256_bits_aqui

# No docker-compose.yml, referencie:
# env_file: .env

# Ou por variável individual:
# environment:
#   POSTGRES_USER: ${POSTGRES_USER}

# Verificar quais variáveis estão sendo resolvidas:
docker compose config

Versões fixas de imagem em produção

Nunca use :latest em produção, uma atualização automática pode quebrar seu serviço silenciosamente:

bash
# Ruim — :latest pode mudar a qualquer momento
image: postgres:latest

# Bom — versão major/minor fixa, patch automático
image: postgres:16

# Melhor — versão completamente fixa com digest (imutável)
image: postgres:16.3

Estratégia de deploy sem downtime

Para deploys com downtime mínimo usando Compose puro (sem Swarm/Kubernetes):

bash
# Pull da nova imagem enquanto a versão atual ainda está rodando
docker compose pull app

# Recria apenas o serviço atualizado (outros serviços continuam)
docker compose up -d --no-deps app

# Verificar se o healthcheck passou antes de considerar o deploy OK
docker compose ps
docker inspect app_container_1 | grep -A5 '"Health"'

Não quer configurar manualmente?

Implante o Docker em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

Com restart: always no Compose e o Docker daemon configurado para iniciar no boot (systemctl enable docker). Todos os containers com restart: always sobem automaticamente quando o Docker daemon iniciar.

docker-compose (com hífen) é a versão 1 do Compose, escrita em Python e instalada separadamente, está obsoleta. docker compose (sem hífen, v2) é um plugin do CLI Docker escrito em Go. Use sempre docker compose (v2). A Runstack pré-instala o Docker com o plugin v2.

Sim, em produção. Sem rede customizada, todos os serviços do Compose ficam na mesma rede padrão (bridge) e podem se comunicar livremente. Com redes customizadas, você isola grupos de serviços, ex.: a app pode falar com o banco, mas o banco não pode iniciar conexões com a app.

Adicione seu usuário ao grupo docker: sudo usermod -aG docker $USER, depois faça logout e login novamente (ou newgrp docker para a sessão atual). Na Runstack, o usuário padrão já está no grupo docker.

docker compose config valida e exibe o compose resolvido (com variáveis substituídas). docker compose config --quiet retorna exit code 0 se válido. Use isso em pipelines CI antes de fazer deploy.