Isso não é um problema do Docker Compose. O Compose fez exatamente o que foi projetado para fazer. O problema é que, em algum ponto entre o seu notebook e o cluster, a equipe começou a tratar o compose.yml como se fosse uma especificação de produção, em vez do que ele realmente é: uma ferramenta ótima para desenvolvimento local.
O que o Docker Compose realmente faz bem
Antes de entrarmos em onde isso desanda, vale reconhecer o quanto o Compose faz bem o trabalho que realmente importa. Subir um ambiente com vários serviços costumava significar um README com quinze passos, metade desatualizados. Agora é um arquivo e um comando. Alguém novo entra na equipe, clona o repositório, roda docker compose up, e na hora do almoço já tem todo o stack rodando localmente, igual ao resto da equipe.
Essa consistência tem um valor real. O Compose mata o clássico "na minha máquina funciona" no nível individual. Cada pessoa da equipe tem a mesma versão do Postgres, as mesmas variáveis de ambiente, a mesma topologia de serviços, sem que ninguém precise configurar nada na mão. Para iterar localmente, para o onboarding, para rodar testes de integração contra dependências reais em vez de mocks, é realmente uma das melhores ferramentas do ecossistema. Nada do que vem a seguir é um argumento contra usá-lo assim.
Onde o compose.yml se transforma, em silêncio, em um contrato de produção
O problema começa com uma ideia que soa bastante razoável: "já temos todos os serviços definidos aqui, por que não usar esse mesmo arquivo para o deploy?" Alguém adiciona alguns overrides de variáveis de ambiente, aponta para um VPS ou um único host Docker, e funciona. Continua funcionando por um tempo, então fica.
O que realmente aconteceu é que um arquivo desenhado para responder "como eu rodo isso no meu notebook?" acabou respondendo "como a gente roda isso na frente dos clientes?" sem que ninguém tivesse decidido isso de propósito. Ninguém se sentou para dizer "vamos pular a orquestração como deveria ser". Foi se acumulando, um deploy de cada vez, até o arquivo de docker compose virar o mais próximo de uma especificação de infraestrutura que a equipe tinha, e a essa altura ninguém queria ser quem diria que ele não foi pensado para aquilo.
Esse é o verdadeiro modo de falha. Não que o Compose tenha sido usado, mas que foi usado além do limite para o qual foi desenhado, e a equipe só percebeu que tinha cruzado esse limite quando algo quebrou.
A brecha entre "funciona" e "está de verdade em produção"
É aqui que a brecha aparece, na prática, porque quase nunca é o código. É tudo aquilo que o Compose abstrai para te dar uma experiência local fluida.
A rede é a primeira coisa. O Compose dá a cada serviço uma rede plana onde qualquer um pode falar com qualquer um pelo nome do serviço, sem perguntas. A produção, se de fato é produção, tem políticas de rede, security groups, service meshes, ou no mínimo um load balancer entre coisas que nunca deveriam ser diretamente alcancáveis. O serviço que resolvia api:8080 instantaneamente na sua máquina esbarra num muro de regras de DNS e firewall nas quais nunca precisou pensar antes.
Os limites de recursos são a segunda coisa. Suas configurações locais de mem_limit e cpus, se você chegou a defini-las, são estimativas feitas numa máquina com muito mais folga do que qualquer nó de produção vai ter. Kubernetes ou ECS aplicam limites reais, com consequências reais: OOM kills, throttling, pods despejados porque pediram mais do que o scheduler podia dar. Um contêiner que rodava tranquilo com 200MB localmente pode se comportar de forma completamente diferente sob carga real e pressão de memória real.
Os segredos são a terceira coisa, e a mais perigosa, porque é o que mais provavelmente parece estar tudo bem até deixar de estar. Os setups locais de Compose carregam segredos de um arquivo .env que fica ali mesmo no repositório, ou direto hardcoded no compose file por conveniência. Isso é ok num notebook ao qual mais ninguém tem acesso. A situação muda completamente quando esse mesmo padrão é copiado num script de deploy que aponta para um servidor real, porque agora existem credenciais em texto puro num lugar onde nunca deveriam ter ficado.
E o service discovery é a quarta coisa. O DNS por nome de serviço do Compose é elegante, e também não tem nada a ver com como um orquestrador de verdade encontra e roteia para instâncias saudáveis através de vários nós, com health checks, rolling restarts, e instâncias que aparecem e desaparecem. No momento em que você tem mais de um nó, ou mais de uma réplica de um serviço, as suposições embutidas na rede do Compose deixam de valer, e nada no arquivo te avisa disso.
Nada disso é um bug do Docker Compose. É um descompasso entre o que a ferramenta promete (um ambiente local que funciona) e o que a equipe passou a esperar dela (um runtime equivalente à produção).
O Docker Compose nunca foi desenhado para orquestrar nada
Vale dizer sem rodeios: o Compose não tem nenhum conceito de cluster. Ele não reagenda um contêiner caído para outro nó, porque no seu modelo não existe outro nó. Não faz rolling deploys, não lida com redes multi-host, não tem um gerenciador de segredos embutido, e nunca tentou ter. Comparar isso com Kubernetes ou ECS não é uma disputa justa, porque eles resolvem problemas diferentes. Um responde "como eu rodo esse stack agora, nesta máquina, para poder construir em cima dele?". O outro responde "como eu mantenho isso rodando, corretamente, através de falhas, escala e tempo, em infraestrutura que eu não controlo totalmente?".
Chamar o compose.yml de infraestrutura como código é onde a confusão começa. Infra como código implica que o arquivo é a fonte de verdade de como o sistema roda no ambiente que importa para os usuários. O Compose nunca tentou ser isso. É uma ferramenta de developer experience, e muito boa, que além disso usa uma sintaxe parecida o suficiente com um manifesto do Kubernetes para que, sob pressão de prazo, as equipes se convençam de que o salto é menor do que realmente é.
O que realmente fecha essa brecha
Nada disso significa jogar o Compose no lixo ou se sentir mal por usá-lo. Significa ser explícito sobre para que ele serve. Mantenha-o como a ferramenta rápida, descartável e voltada ao desenvolvedor que ele faz bem, e trate o destino real do deploy como um assunto à parte, com seu próprio manifesto (seja YAML de Kubernetes, uma task definition do ECS ou um job spec do Nomad), escrito para o orquestrador sobre o qual você realmente roda.
As equipes que escapam do incidente das 11 da noite não são as que encontraram uma forma engenhosa de reaproveitar o compose.yml em produção. São as que aceitaram cedo que local e produção são ambientes diferentes resolvendo problemas diferentes, e construíram duas coisas separadas, com nomes honestos, em vez de um único arquivo cumprindo função dupla em silêncio. As imagens do Docker Compose podem continuar sendo as mesmas imagens promovidas por um registry, as variáveis de ambiente podem continuar sendo definidas uma única vez e templatizadas, mas a camada de orquestração em si (o modelo de rede, a aplicação de limites de recursos, o gerenciamento de segredos) precisa da sua própria definição, revisada com a mesma seriedade que o código da aplicação.
Um ambiente de staging que realmente reflita a produção (mesmo orquestrador, mesma topologia de rede, mesmos tetos de recursos) é o que detecta a brecha antes que um cliente a detecte. Não precisa ser caro. Precisa ser honesto sobre rodar sobre o real, em vez de uma aproximação local. Se o seu staging também é só docker compose up num servidor maior, você não fechou a brecha, só a empurrou um passo mais longe do seu notebook.
Existe um sinal mais simples para ficar de olho. Se ninguém na equipe consegue apontar o manifesto real de produção sem também apontar o compose.yml, isso não é uma lacuna de documentação. É a brecha já em movimento, e vale a pena sinalizar isso num standup antes que vire um incident report. Consertar isso custa uma tarde escrevendo um manifesto de verdade. A alternativa custa uma noite debugando em produção enquanto os clientes assistem.
A postura real aqui
Docker Compose não é uma versão leve da infraestrutura de produção. É uma categoria de ferramenta completamente diferente, e o momento em que uma equipe começa a tratá-lo como se fosse é o momento em que uma brecha silenciosa começa a se acumular entre o que os engenheiros veem localmente e o que realmente roda por trás de um domínio. A solução não é um compose file melhor. É admitir que uma ferramenta de developer experience e uma especificação de infraestrutura não são o mesmo artefato, por mais parecido que o YAML pareça, e se recusar a deixar que a pressão de prazo borre essa linha até um incidente de produção redesenhá-la por você.




