DevOps

    Docker Compose em Produção: O Checklist Que Separa Hobby de Operação Séria

    Docker Compose serve produção — com as configurações certas. Checklist completo: healthchecks, secrets, TLS, backup, logs e os erros que derrubam sistemas em produção.

    2025-07-0811 minEquipe MaxVision
    CLIP_001 · DJI O4FPV · 4K · 60FPS

    A maioria dos servidores em produção que "parou do nada" tinha uma coisa em comum: Docker sem healthchecks. Docker Compose é frequentemente descartado como ferramenta de desenvolvimento — e essa percepção leva equipes a adotar Kubernetes antes de precisar, ou a operar Compose em produção da mesma forma que testam localmente, sem as configurações que fazem a diferença entre um servidor que aguenta e um que cai silenciosamente.

    Resumo rápido: Docker Compose é uma escolha legítima para produção em operações com até dezenas de serviços num único servidor ou em poucos servidores. A diferença entre hobby e operação séria está em configurações específicas: restart policies, healthchecks, volumes nomeados, secrets fora do repositório, reverse proxy com TLS, backup automatizado e logs centralizados. Nenhum desses itens é complexo — mas todos são ignorados com frequência.

    Diagrama mostrando stack Docker Compose de produção com Traefik, healthchecks, volumes e Restic backup

    Por que Docker Compose ainda é relevante em produção

    Antes de entrar no checklist, vale estabelecer em que contexto o Compose faz sentido em produção.

    Compose é adequado quando você tem um conjunto de serviços que rodam no mesmo servidor (ou em poucos servidores com deploy independente), um time pequeno sem necessidade de isolamento por namespace, e cargas que não exigem autoscaling horizontal automático entre múltiplos nodes.

    Nesses cenários — que representam boa parte das operações reais — Compose entrega simplicidade operacional que Kubernetes nunca vai entregar. O arquivo docker-compose.yml é legível por qualquer pessoa que entenda Docker. O deploy é um único comando. O rollback também.

    O problema não é o Compose. O problema é rodar Compose em produção sem as configurações que transformam um ambiente de desenvolvimento funcional numa operação que resiste a falhas, escala dentro dos limites do servidor e pode ser auditada quando algo der errado.

    Item 1: Restart Policy — o mínimo que todo serviço precisa

    A configuração mais simples e mais ignorada. Sem restart: unless-stopped, um container que para por qualquer motivo fica parado até alguém notar.

    services:
      api:
        image: minha-api:latest
        restart: unless-stopped
    

    Use unless-stopped em vez de always para manter controle: containers parados manualmente não sobem sozinhos, mas containers que pararam por erro reiniciam automaticamente. Isso evita que uma parada planejada para manutenção vire reinicialização em loop.

    Para serviços críticos onde você quer reinicialização agressiva mesmo após parada manual, always faz sentido — mas documente a escolha. Nos deploys que o departamento de DevOps da MaxVision entrega, essa decisão vai direto para o runbook de cada serviço.

    Item 2: Healthchecks — o que separa "container rodando" de "serviço funcionando"

    Um container pode estar "up" e o processo dentro dele pode estar travado, em loop de erro, ou respondendo com 500 para tudo. O Docker não sabe a diferença sem um healthcheck.

    services:
      api:
        image: minha-api:latest
        restart: unless-stopped
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
          interval: 30s
          timeout: 10s
          retries: 3
          start_period: 40s
    

    O start_period é crucial para serviços que demoram para inicializar (JVM, conexões com banco na inicialização). Sem ele, o Docker começa a contar falhas antes do serviço estar pronto e pode reiniciá-lo desnecessariamente.

    Healthchecks também habilitam a diretiva depends_on com condição service_healthy, permitindo que serviços dependentes só subam quando suas dependências estiverem prontas de fato — não apenas "iniciadas".

    Item 3: Volumes Nomeados — dados que sobrevivem ao container

    Dados em containers sem volumes desaparecem quando o container é removido. Para banco de dados, uploads, logs persistentes e qualquer dado que precisa sobreviver a um deploy, volumes nomeados são obrigatórios.

    services:
      postgres:
        image: postgres:16
        volumes:
          - postgres_data:/var/lib/postgresql/data
        restart: unless-stopped
    
    volumes:
      postgres_data:
        driver: local
    

    Volumes nomeados são gerenciados pelo Docker e sobrevivem a docker-compose down. Bind mounts (mapeamento direto de diretório do host) são aceitáveis para configurações e arquivos de código, mas para dados de aplicação use volumes nomeados: eles são portáveis, têm backup mais simples e não criam dependência de path absoluto no host.

    Item 4: Secrets fora do repositório — o erro mais caro

    Secrets no docker-compose.yml comprometido no repositório é um dos vetores mais comuns de comprometimento de credenciais. O padrão correto usa variáveis de ambiente via arquivo .env que não entra no repositório, ou o mecanismo nativo de secrets do Docker.

    O .env precisa obrigatoriamente estar no .gitignore. O repositório deve conter um .env.example com as variáveis listadas mas sem valores reais:

    # .env.example (entra no git)
    POSTGRES_PASSWORD=
    JWT_SECRET=
    API_KEY=
    
    # .env (nunca entra no git — .gitignore obrigatório)
    POSTGRES_PASSWORD=s3nh@_real_aqui
    JWT_SECRET=jwt_secret_real
    API_KEY=chave_real
    

    Para ambientes de produção com múltiplos operadores, o Docker Secrets é mais seguro: os valores ficam criptografados no swarm state e não aparecem em docker inspect. Em Compose puro (sem Swarm), a abordagem com .env é aceitável desde que o arquivo nunca vá ao repositório e o servidor tenha as permissões corretas (apenas root e o usuário da aplicação lêem o arquivo).

    Item 5: Reverse Proxy com TLS — Traefik ou Caddy

    Expor serviços diretamente na porta 80/443 sem reverse proxy é uma combinação de problemas: sem roteamento por hostname, sem SSL automático, sem rate limiting centralizado, sem logging de acesso unificado.

    Traefik e Caddy são as duas escolhas mais comuns para Compose. O Caddy tem a curva de aprendizado menor e configura SSL automático via Let's Encrypt com o arquivo Caddyfile mais conciso que existe. O Traefik tem integração nativa com labels do Docker, o que facilita adicionar serviços sem editar configuração central.

    Exemplo minimalista com Caddy:

    services:
      caddy:
        image: caddy:2
        restart: unless-stopped
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./Caddyfile:/etc/caddy/Caddyfile
          - caddy_data:/data
          - caddy_config:/config
    
      api:
        image: minha-api:latest
        restart: unless-stopped
        # sem ports expostos — apenas Caddy acessa
    
    volumes:
      caddy_data:
      caddy_config:
    

    O Caddyfile:

    api.exemplo.com {
        reverse_proxy api:3000
    }
    

    Isso cobre renovação automática de SSL, redirect HTTP→HTTPS e headers de segurança básicos por padrão. O Let's Encrypt faz a emissão e renovação de certificados sem intervenção manual.

    Fluxo de request com Caddy como reverse proxy, TLS automático e múltiplos serviços internos no Compose

    Item 6: Backup com Restic — o que salva quando tudo mais falha

    Backup não é opcional em produção. Um servidor comprometido, um docker volume rm acidental, uma falha de disco — tudo isso é questão de quando, não de se.

    Restic é a ferramenta de backup mais sólida para esse contexto: deduplicação, criptografia por padrão, suporte a múltiplos backends (S3, B2, SFTP, local) e verificação de integridade dos backups.

    Para dados de banco de dados, o backup precisa ser do dump lógico, não apenas do volume — um volume com banco corrompido gera um backup de dado corrompido.

    # backup postgres
    docker exec postgres pg_dump -U postgres meudb > /backups/db_$(date +%Y%m%d_%H%M%S).sql
    
    # backup com Restic
    restic -r s3:s3.amazonaws.com/meu-bucket backup /backups \
      --password-file /etc/restic-password
    

    Automatize via cron e monitore os logs. Um backup que falha silenciosamente não é backup — é falsa segurança. Configure alertas para quando o job de backup não executa ou quando encontra erros.

    Item 7: Logs centralizados — rastrear incidentes sem SSH em produção

    docker logs no container individual não escala. Em produção, você precisa de logs acessíveis, indexados e com retenção definida.

    A opção mais simples é o driver de log do Docker com rotação:

    services:
      api:
        image: minha-api:latest
        logging:
          driver: "json-file"
          options:
            max-size: "50m"
            max-file: "5"
    

    Sem max-size, os logs de um serviço verboso podem consumir todo o disco do servidor — e esse é um modo de falha que acontece de forma gradual até que de repente para tudo.

    Para centralização real, Loki + Promtail é a stack mais comum em Compose: o Promtail coleta logs de todos os containers e envia ao Loki; o Grafana visualiza e permite busca. É mais trabalho para configurar, mas entrega investigação de incidentes sem precisar acessar o servidor diretamente.

    O checklist consolidado

    Item do ChecklistRisco se IgnorarComo a MaxVision Resolve
    Restart policy unless-stoppedContainer para e fica parado sem alertaPadrao em todos os servicos desde o primeiro deploy
    Healthcheck por servicoContainer "up" com aplicacao travada, nenhum alertaEndpoint /health padronizado + healthcheck no Compose
    Volumes nomeados para dadosDados perdidos em docker-compose downSeparacao entre config (bind) e dados (volume nomeado)
    Secrets via .env fora do gitCredenciais comprometidas no repositorio.env.example no git, .env real no servidor, rotation documentada
    Reverse proxy TLS (Caddy/Traefik)Servico exposto sem SSL, sem roteamento, sem rate limitCaddy ou Traefik padrao com SSL Let's Encrypt automatico
    Backup Restic automatizadoPerda total de dados em falha de disco ou erro operacionalJob cron diario + verificacao semanal de integridade
    Log rotation configuradaDisco cheio por logs, derruba servidor inteiromax-size e max-file em todos os servicos
    Usuario nao-root no containerProcesso comprometido tem acesso root ao hostUSER no Dockerfile, nunca rodar como root
    Network isolationTodos os containers se veem por padraoNetworks nomeadas separando camadas (web, app, db)
    Monitoramento basicoSem visibilidade de CPU/mem/disco antes do problemaPrometheus + Grafana ou UptimeRobot para alertas externos

    Os erros que derrubam sistemas em produção

    chmod 777 em diretórios de dados. É o atalho para "resolver permissão agora" que cria superfície de ataque para qualquer processo que ganhe execução de código. Use o usuário correto no Dockerfile e defina permissões precisas.

    Banco de dados sem autenticação na rede interna. Em Compose, serviços na mesma network se veem. Se o banco não tem senha, qualquer container comprometido lê todos os dados.

    Secrets no repositório git. Uma credencial commitada no histórico do git permanece lá mesmo após remoção — qualquer clone feito antes inclui a credencial. A única solução após um commit de secret é rotacionar todas as credenciais expostas.

    Deploy sem testar o rollback. O deploy em Compose é simples; o rollback também — mas precisa ter sido testado antes do incidente. Documente o procedimento de rollback e valide que ele funciona antes de precisar usá-lo sob pressão.

    Sem monitoramento de disco. Containers geram logs, banco cresce, uploads acumulam. Disco cheio é um dos modos de falha mais comuns e mais evitáveis. Configure alertas para quando o disco ultrapassa determinado percentual.

    Perguntas Frequentes

    Docker Compose aguenta tráfego alto em produção?

    Depende do que você considera "alto". Uma aplicação web com alguns milhares de usuários simultâneos pode rodar confortavelmente em Compose num servidor bem dimensionado. O Compose não é o gargalo — o servidor é. A limitação do Compose é que ele não distribui containers entre múltiplos servidores automaticamente. Se você precisa de múltiplos servidores, avalie K3s ou Kubernetes. Para um único servidor poderoso, Compose é suficiente para volumes consideráveis.

    Traefik ou Caddy para iniciantes?

    Caddy. O Caddyfile é mais conciso e o SSL automático funciona sem configuração adicional. Traefik tem mais poder de configuração e integração nativa com Docker labels, mas a curva de aprendizado é maior. Comece com Caddy, migre para Traefik quando você tiver necessidades específicas que o Caddy não resolve.

    É necessário backup de volumes se eu uso banco gerenciado (RDS, Supabase)?

    Se o banco é gerenciado, os backups do banco são responsabilidade do provedor — mas você ainda precisa de backup de outros volumes: uploads de usuários, arquivos gerados, configurações customizadas. Banco gerenciado não elimina a necessidade de backup, apenas muda o que você precisa fazer backup.

    Como fazer deploy com zero downtime em Compose?

    Compose não tem zero-downtime deploy nativo como Kubernetes. O padrão mais simples é usar Traefik com múltiplas réplicas do serviço: você sobe a nova versão como um segundo container enquanto o antigo ainda atende, e o Traefik distribui a carga automaticamente. O processo requer mais configuração do que um deploy K8s, mas é viável. Para serviços stateless, é a abordagem recomendada.

    Preciso de root para rodar Docker em produção?

    Não, e evitar root é recomendado. Docker tem suporte a modo rootless desde a versão 20.10. Rodar o daemon Docker como usuário não-privilegiado reduz a superfície de ataque em caso de escape de container. Em servidores dedicados, o modo rootless é a configuração que recomendamos.

    Conclusão

    Docker Compose em produção não é demérito — é pragmatismo. A ferramenta resolve a maioria dos problemas reais que startups e operações em crescimento enfrentam, desde que configurada com os itens que fazem a diferença entre um ambiente que aguenta e um que surpreende negativamente às 3h da manhã.

    O checklist acima não é exaustivo, mas cobre os itens que aparecem com mais frequência em pós-mortems de incidentes: falta de healthcheck que mascara falhas, secrets expostos, disco cheio por log sem rotação, backup que nunca foi testado.

    Montar essa stack corretamente — Caddy ou Traefik no lugar certo, backup Restic com verificação periódica, log rotation e hardening documentados — é o trabalho que o departamento de DevOps da MaxVision entrega com runbook de incidentes e SLA por escrito. Para dar o próximo passo, fale com a gente.

    Posts Relacionados

    TAGS
    • DevOps
    • Docker
    • Infraestrutura
    • Checklist
    • Seguranca
    Fale agora pelo WhatsApp