Contêiner Docker inicia e encerra? Guia completo para Exit Code 137 e 1

Eu já estava me preparando para desligar o computador e ir embora quando o celular vibrou de repente: era um alerta do ambiente de produção. Ao abrir a notificação, vi que os contêineres dos quatro serviços principais estavam com status Exited. Abri o terminal e digitei docker ps. Nada. Uma tela completamente vazia.
A sensação era a mesma de abrir a geladeira para pegar uma bebida e descobrir que ela estava totalmente vazia. Bateu o desespero.
Para ser sincero, a primeira coisa que pensei foi: “Pronto, perdi o fim de semana”. Depois de me acalmar, porém, percebi que aquela não era a primeira vez que eu enfrentava uma falha na inicialização de contêineres. O problema apenas tinha aparecido de forma mais repentina e com um impacto maior.
Depois de mais de duas horas de investigação, descobri que a causa era bem simples: o caminho do arquivo de configuração de um dos serviços estava errado. Isso fazia a conexão com o banco de dados dependente falhar, e o contêiner encerrava imediatamente após iniciar. Se eu tivesse um processo sistemático de diagnóstico naquele momento, provavelmente teria resolvido tudo em dez minutos.
Este artigo reúne o guia que preparei depois de cair em inúmeros desses problemas. Não importa se você encontrou o Exit Code 1, 137 ou outro código: este método ajuda a localizar rapidamente a causa da falha.
Entenda o ciclo de vida do contêiner e os códigos de saída
Antes de começar o diagnóstico, precisamos esclarecer uma questão básica: por que um contêiner encerra?
A essência de um contêiner: o ciclo de vida de um processo
Em termos simples, um contêiner Docker é um processo isolado. Enquanto esse processo está ativo, o contêiner continua em execução; quando o processo termina, o contêiner também encerra.
Imagine que você inicializou um contêiner com um serviço web. O processo principal dentro dele pode ser o nginx ou o node. Enquanto esse processo estiver em execução, o contêiner aparecerá em docker ps. Mas, se o processo principal terminar por qualquer motivo — conclusão normal, falha ou encerramento forçado pelo sistema —, o contêiner mudará imediatamente para o status Exited.
É por isso que às vezes docker ps não mostra nada e você precisa acrescentar o parâmetro -a para ver os contêineres que já encerraram.
Tabela rápida de códigos de saída: a história por trás dos números
Sempre que um contêiner encerra, o Docker registra um código de saída, ou Exit Code. O número pode parecer misterioso, mas ele indica o que aconteceu.
Exit Code 0: tudo correu normalmente e a tarefa foi concluída.
Por exemplo, se você executar um script de importação de dados, é natural que ele encerre após terminar o trabalho. Nesse caso, o código será 0. Isso não é um problema: o contêiner apenas concluiu sua função.
Exit Code 1: o próprio programa encontrou um problema.
Esse é o código de erro mais comum. Pode haver um erro no arquivo de configuração, uma dependência ausente ou um bug no código. Em resumo, a aplicação dentro do contêiner falhou.
Lembro de uma implantação de um contêiner MySQL que encerrava sempre com Exit Code 1. Depois de analisar os logs por um bom tempo, descobri que eu havia trocado sem querer um sinal de igual por dois-pontos no arquivo de configuração. Ao iniciar, o MySQL detectava o erro de sintaxe e parava imediatamente.
Exit Code 137: faltou memória ou o processo foi encerrado à força.
Esse é o código que eu menos gosto de ver. O 137 geralmente indica uma de duas situações:
- O contêiner ultrapassou a memória disponível, e o OOM Killer do kernel Linux encerrou o processo.
- Alguém — ou o sistema — executou
docker killoukill -9.
Como diferenciar os casos? Use docker inspect e consulte o campo OOMKilled. Se o valor for true, o problema é memória; se for false, o processo pode ter sido encerrado manualmente.
Exit Code 127: comando não encontrado.
Normalmente, o caminho definido em CMD ou ENTRYPOINT no Dockerfile está incorreto, ou o arquivo executável simplesmente não existe na imagem do contêiner.
Exit Code 139: falha de segmentação, ou Segmentation Fault.
Isso costuma ocorrer em programas C/C++ e indica que o programa tentou acessar um endereço de memória inválido. Se você não executa software de baixo nível, dificilmente encontrará esse código.
O padrão dos códigos de saída
Os códigos seguem um padrão:
- 0: encerramento normal, sem problemas.
- 1-128: problema do próprio programa, como erro da aplicação ou de configuração.
- 129-255: intervenção externa, como interrupção por sinal ou encerramento pelo sistema.
Depois de entender esse padrão, o código de saída já permite identificar aproximadamente o tipo de problema e direcionar as próximas etapas do diagnóstico.
Diagnóstico em quatro etapas para localizar rapidamente o problema
Agora você conhece o significado dos códigos de saída. Mas isso não basta: também é preciso saber como investigar o problema passo a passo.
Com o tempo, cheguei a um processo de quatro etapas que cobre cerca de 90% das falhas de inicialização de contêineres. Ao seguir esse fluxo, você perceberá que o problema costuma ser menos misterioso do que parece.
Etapa 1: confirme o status do contêiner
Não comece pelos logs. Primeiro, confirme que o contêiner existe e realmente encerrou.
docker ps -a
Esse comando lista todos os contêineres, inclusive os que já encerraram. Observe principalmente estas informações:
CONTAINER ID: identificador único do contêiner, usado nos comandos seguintes. Você pode copiar apenas os primeiros caracteres, pois o Docker faz a correspondência automaticamente.
Coluna STATUS: esta é a parte mais importante. Um contêiner em execução mostra Up X minutes; um contêiner encerrado mostra Exited (código de saída) X minutes ago.
Por exemplo:
CONTAINER ID IMAGE STATUS
a1b2c3d4e5f6 mysql:8.0 Exited (1) 2 minutes ago
O código de saída é 1, então o problema está na camada da aplicação. Se fosse 137, poderia ser falta de memória.
Observe também os horários de criação e encerramento do contêiner. Se ele encerrou menos de um segundo depois de ser criado, o comando de inicialização ou a configuração provavelmente está incorreto. Se funcionou por algum tempo antes de encerrar, pode haver falta de recursos ou falha de um serviço dependente.
Etapa 2: consulte os logs do contêiner
Esta é a etapa mais importante. Antes de encerrar, o contêiner geralmente deixa pistas, e elas ficam nos logs.
Consulta básica:
docker logs <container_id>
O comando mostra toda a saída padrão e a saída de erro do contêiner. Muitas vezes, você verá diretamente mensagens como Permission denied, No such file or directory ou Connection refused.
Acompanhamento em tempo real — útil para analisar a inicialização:
docker logs -f <container_id>
Para observar o que acontece durante a inicialização, use o parâmetro -f. Ele exibe novas linhas em tempo real, como tail -f. Esse recurso não é tão útil para um contêiner que já encerrou, mas ajuda bastante ao tentar iniciá-lo novamente.
Apenas os logs mais recentes:
docker logs --tail 100 <container_id>
Se os logs forem muito longos, consulte somente as últimas 100 linhas. Em muitos casos, a causa aparece justamente no final.
Com timestamps:
docker logs -t <container_id>
O parâmetro -t adiciona um timestamp a cada linha e ajuda a determinar exatamente quando o problema ocorreu.
Filtrar logs de erro:
docker logs <container_id> 2>&1 | grep -i error
Se houver informação demais, filtre somente as linhas que contêm “error”. É uma maneira rápida de encontrar as mensagens principais.
Etapa 3: verifique a configuração do contêiner
Às vezes, nem os logs deixam a causa clara. Nesse caso, é preciso examinar a configuração e o estado do contêiner com mais detalhes.
Ver a configuração completa:
docker inspect <container_id>
Esse comando produz muitas informações em formato JSON, incluindo todas as configurações, variáveis de ambiente, pontos de montagem e ajustes de rede do contêiner. É bastante conteúdo, mas também pode ser muito útil.
Consultar rapidamente informações específicas:
Ver o código de saída:
docker inspect --format '{{.State.ExitCode}}' <container_id>
Ver se o processo foi encerrado pelo OOM Killer:
docker inspect --format '{{.State.OOMKilled}}' <container_id>
Se o resultado for true, você confirmou que o problema é falta de memória.
Ver as variáveis de ambiente:
docker inspect --format '{{.Config.Env}}' <container_id>
Às vezes, uma variável de ambiente está incorreta, como a string de conexão do banco de dados ou uma chave de API.
Ver os caminhos de montagem:
docker inspect --format '{{.Mounts}}' <container_id>
Confirme se os arquivos de configuração e diretórios de dados foram montados corretamente.
Ver o caminho do arquivo de log:
docker inspect --format='{{.LogPath}}' <container_id>
Se nem docker logs funcionar bem, você poderá localizar o arquivo de log diretamente no host.
Etapa 4: valide a inicialização interativamente
Você concluiu as três etapas anteriores e ainda não encontrou a causa? Então é hora de entrar no contêiner e verificar pessoalmente.
Inicie o contêiner interativamente:
Se o comando original era:
docker run -d my-app
Troque -d por -it para executar o contêiner em primeiro plano:
docker run -it my-app
Assim, você verá em tempo real toda a saída do processo de inicialização, e muitos erros aparecerão diretamente na tela.
Entre manualmente no contêiner:
Se o contêiner inicia e encerra imediatamente, abra um shell para executar os comandos à mão:
docker run -it my-app /bin/bash
Ou:
docker run -it my-app /bin/sh
Depois de entrar, você pode:
- Confirmar se o arquivo de configuração existe:
ls /etc/app/config.yaml. - Testar a sintaxe da configuração: por exemplo,
mysqld --verbose --helpverifica a configuração do MySQL. - Executar o comando de inicialização manualmente e observar o erro exato.
- Verificar a conectividade com serviços dependentes:
ping databaseetelnet redis 6379.
Pode parecer um processo longo, mas, na prática, muitos problemas são resolvidos na segunda etapa, ao consultar os logs. Apenas os casos mais difíceis exigem as quatro etapas completas.
Cinco cenários comuns de falha e suas soluções
Depois de conhecer o método de diagnóstico, vamos aos casos mais frequentes na prática. Eu os agrupei em cinco categorias que cobrem a maioria dos problemas cotidianos.
Cenário 1: erro no arquivo de configuração ou caminho inexistente
Sintomas típicos:
- Exit Code 1.
- Os logs mostram mensagens como
No such file or directory,config file not foundousyntax error.
Caso real:
Certa vez, implantei uma aplicação Node.js, mas o contêiner não conseguia iniciar. Os logs mostravam:
Error: ENOENT: no such file or directory, open '/app/config/prod.json'
Ao investigar, descobri que o caminho de montagem no comando docker run era:
-v /home/user/config:/app/conf # Observe que aqui está conf
Mas a aplicação lia o caminho /app/config. Por causa de uma única palavra diferente, ela não encontrava o arquivo de configuração e a inicialização falhava.
Como diagnosticar:
- Use
docker inspect --format '{{.Mounts}}'para verificar os caminhos de montagem. - Entre no contêiner e use
lspara confirmar se o arquivo realmente está no local esperado. - Se houver erro de sintaxe no arquivo de configuração, a maioria das aplicações indicará a linha problemática nos logs.
Solução:
Caminho de montagem incorreto:
# Exemplo incorreto
docker run -v /host/path:/wrong/path my-app
# Forma correta
docker run -v /host/path:/app/config my-app
Erro de sintaxe no arquivo de configuração:
- Para arquivos YAML, use uma ferramenta online ou
yamllintpara verificar a sintaxe. - Para arquivos JSON, valide com
jq:jq . config.json. - Para a configuração do MySQL, execute
mysqld --verbose --helpno contêiner para detectar erros de sintaxe.
Cenário 2: memória insuficiente — OOM Killed
Sintomas típicos:
- Exit Code 137.
docker inspect --format '{{.State.OOMKilled}}'retornatrue.- Os logs podem mostrar mensagens como
Cannot allocate memoryouOut of memory.
Caso real:
Eu tinha uma aplicação Java que funcionava normalmente no ambiente local, mas reiniciava sem parar quando era implantada no servidor de testes. Os logs mostravam:
OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory failed; error='Cannot allocate memory' (errno=12)
Descobri que o Docker Desktop no servidor de testes tinha um limite de apenas 512 MB, enquanto a aplicação Java consumia 600 MB já na inicialização.
Como diagnosticar:
# Confirme se foi OOM
docker inspect --format '{{.State.OOMKilled}}' <container_id>
# Consulte a memória do host
free -h
# Veja o uso de memória do contêiner em execução
docker stats <container_id>
Solução:
Aumente o limite de memória do contêiner:
docker run -m 1g my-app # Limite máximo de memória de 1 GB
docker run -m 512m --memory-swap 1g my-app # Define também a swap
Se você usa o Docker Desktop, ajuste a configuração:
- macOS: Docker Desktop → Preferences → Resources → Memory.
- Windows: Docker Desktop → Settings → Resources → Memory.
Otimize a própria aplicação:
- Em aplicações Java, limite o heap da JVM:
java -Xmx512m -jar app.jar. - Em Node.js, use:
node --max-old-space-size=512 app.js. - Verifique se há vazamentos de memória no código.
Recomendações para produção:
- Defina um limite de memória adequado com base nas necessidades reais da aplicação.
- Configure
--memory-reservationcomo limite flexível. - Monitore a tendência de uso de memória e amplie os recursos com antecedência.
Cenário 3: conflito de porta
Sintomas típicos:
- Exit Code 1.
- Os logs mostram
port is already allocated,address already in useoubind: address already in use.
Caso real:
Em uma segunda-feira de manhã, executei docker-compose up, mas o contêiner do Nginx não iniciou. A mensagem de erro era:
Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use
Na sexta-feira anterior, eu havia testado um Nginx local e esquecido de interrompê-lo. A porta 80 continuava ocupada, então o novo contêiner não podia iniciar.
Como diagnosticar:
Verifique o uso da porta no Linux/macOS:
lsof -i :8080
netstat -tuln | grep 8080
Verifique o uso da porta no Windows:
netstat -ano | findstr 8080
Veja o mapeamento de portas dos outros contêineres:
docker ps --format "table {{.Names}}\t{{.Ports}}"
Solução:
Opção 1: troque a porta mapeada.
# Comando original
docker run -p 8080:8080 my-app
# Use outra porta
docker run -p 8081:8080 my-app
Opção 2: interrompa o serviço que ocupa a porta.
# Encontre o ID do processo
lsof -i :8080
# Encerre o processo
kill -9 <PID>
Opção 3: se outro contêiner estiver usando a porta, interrompa-o primeiro.
docker stop <conflicting_container>
Atenção: ao usar o modo --network=host, o contêiner acessa diretamente a rede do host, o que aumenta a chance de conflitos. Nesse modo, as portas do contêiner não podem conflitar com as portas do host.
Cenário 4: permissões insuficientes
Sintomas típicos:
- Exit Code 1.
- Os logs mostram
Permission denied,Operation not permittedouchown: changing ownership failed.
Caso real:
Ao implantar um contêiner MongoDB, montei o diretório de dados no host. O contêiner, porém, não conseguia iniciar:
chown: changing ownership of '/data/db': Permission denied
Depois descobri que o diretório montado pertencia ao usuário root, enquanto o processo do MongoDB no contêiner era executado pelo usuário mongodb, com UID 999, que não tinha permissão para gravar naquele diretório.
Como diagnosticar:
Verifique as permissões do diretório no host:
ls -la /host/data/path
Veja o usuário dentro do contêiner:
docker run -it my-app /bin/bash
whoami
id
Verifique o SELinux no CentOS/RHEL:
getenforce # Mostra o status do SELinux
Solução:
Opção 1: ajuste as permissões do diretório no host.
# Concede leitura e gravação a todos (pouco seguro; apenas para desenvolvimento)
chmod 777 /host/data/path
# Alternativa mais segura: altere o proprietário
chown -R 999:999 /host/data/path # 999 é o UID do usuário no contêiner
Opção 2: use o modo privilegiado com cautela.
docker run --privileged=true my-app
Observação: o modo privilegiado concede ao contêiner quase todas as permissões do host, o que representa um risco de segurança. Não é recomendado em produção.
Opção 3: especifique o usuário de execução.
docker run --user 1000:1000 my-app # Usa o UID/GID do host
Opção 4: trate os problemas do SELinux.
# Método 1: adicione o rótulo Z (altera o rótulo do arquivo no host)
docker run -v /host/path:/container/path:Z my-app
# Método 2: adicione o rótulo z (rótulo compartilhado)
docker run -v /host/path:/container/path:z my-app
# Método 3: desative temporariamente o SELinux (não recomendado em produção)
setenforce 0
Cenário 5: serviço dependente ainda não está pronto
Sintomas típicos:
- Exit Code 1.
- Os logs mostram falha de conexão com o banco de dados, timeout na conexão com o Redis ou mensagens semelhantes.
Connection refused,ECONNREFUSEDoucould not connect to server.
Caso real:
Ao implantar um conjunto de microsserviços com docker-compose, uma aplicação dependia de um banco de dados MySQL. Os dois contêineres iniciavam quase ao mesmo tempo, mas o da aplicação sempre falhava:
Error: connect ECONNREFUSED 172.18.0.2:3306
O problema era que, embora o contêiner do MySQL já tivesse iniciado, o serviço ainda estava em processo de inicialização e não estava pronto para aceitar conexões. A aplicação iniciava rápido demais, falhava ao conectar e encerrava.
Como diagnosticar:
Confirme se o serviço dependente iniciou:
docker ps # Veja se o contêiner dependente está em execução
Teste a conectividade de rede:
docker exec my-app ping database
docker exec my-app telnet database 3306
docker exec my-app nc -zv database 3306
Consulte a configuração da rede Docker:
docker network ls
docker network inspect <network_name>
Solução:
Opção 1: use a verificação de integridade e depends_on no docker-compose.
version: '3.8'
services:
database:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
app:
image: my-app
depends_on:
database:
condition: service_healthy # Aguarda a verificação de integridade do banco de dados
Opção 2: adicione novas tentativas na camada da aplicação.
Inclua uma lógica de repetição da conexão no código:
// Exemplo em Node.js
async function connectWithRetry(maxRetries = 5) {
for (let i = 0; i < maxRetries; i++) {
try {
await db.connect();
console.log('Database connected');
return;
} catch (err) {
console.log(`Connection failed, retrying... (${i+1}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
throw new Error('Failed to connect to database');
}
Opção 3: use um script de espera na inicialização.
Você pode executar um script, como wait-for-it.sh, antes de iniciar o contêiner:
# No Dockerfile
COPY wait-for-it.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/wait-for-it.sh
# Use na inicialização
CMD ["wait-for-it.sh", "database:3306", "--", "node", "app.js"]
Opção 4: configure uma política de reinicialização.
Faça o contêiner tentar novamente após uma falha:
docker run --restart=on-failure:3 my-app # Reinicia no máximo três vezes após uma falha
No docker-compose:
services:
app:
restart: on-failure
Essas soluções podem ser combinadas. Por exemplo: verificação de integridade, novas tentativas na aplicação e política de reinicialização formam três camadas de proteção.
Medidas preventivas e boas práticas
Até aqui, vimos como corrigir os problemas depois que eles aparecem. Porém, se você configurar alguns mecanismos desde o início, muitas falhas nem acontecerão ou poderão ser recuperadas automaticamente.
Configure verificações de integridade — HEALTHCHECK
A verificação de integridade permite que um contêiner Docker avalie a si próprio. Ao executar periodicamente um comando de verificação, o Docker consegue saber se o contêiner realmente funciona, e não apenas se o processo continua ativo.
Configure-a no Dockerfile:
FROM nginx:alpine
# Verifica a cada 30 segundos, com timeout de 3 segundos; após três falhas, marca como unhealthy
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost/ || exit 1
Para serviços web, verifique um endpoint HTTP:
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s \
CMD curl -f http://localhost:8080/health || exit 1
Para bancos de dados, use comandos específicos:
# MySQL
HEALTHCHECK CMD mysqladmin ping -h localhost || exit 1
# PostgreSQL
HEALTHCHECK CMD pg_isready -U postgres || exit 1
# Redis
HEALTHCHECK CMD redis-cli ping || exit 1
Vantagens das verificações de integridade:
- Orquestradores como Kubernetes e Swarm podem reiniciar ou reagendar contêineres automaticamente conforme o estado de integridade.
- O
depends_ondo docker-compose pode aguardar até que o serviço esteja realmente saudável antes de iniciar um contêiner dependente. - Sistemas de monitoramento podem emitir alertas com base no estado de integridade.
Consulte o estado de integridade:
docker ps # A coluna STATUS mostra o estado de integridade
docker inspect --format='{{.State.Health.Status}}' <container_id>
Defina uma política de reinicialização
As políticas de reinicialização permitem que o contêiner se recupere automaticamente depois de uma falha, sem que você precise acordar de madrugada para reiniciá-lo manualmente.
O Docker oferece quatro políticas:
no — padrão: não reinicia automaticamente.
docker run --restart=no my-app
É adequada para tarefas pontuais, em que o contêiner termina depois de concluir o trabalho.
on-failure[:max-retries]: reinicia somente após um encerramento anormal.
docker run --restart=on-failure:5 my-app # Tenta no máximo cinco vezes
É adequada para serviços que podem falhar, mas não devem tentar para sempre. Observe que a reinicialização ocorre apenas quando o Exit Code é diferente de 0.
always: reinicia sempre.
docker run --restart=always my-app
É adequada para serviços de longa duração, como servidores web e APIs. Mesmo que você execute stop manualmente, o contêiner voltará a iniciar quando o Docker Daemon reiniciar.
unless-stopped: reinicia sempre, exceto depois de uma interrupção manual.
docker run --restart=unless-stopped my-app
É semelhante a always, mas, se você executar docker stop, o contêiner não iniciará automaticamente na próxima reinicialização do Docker Daemon. É a política que uso com mais frequência, pois mantém algum controle manual.
Observações importantes:
- Regra dos 10 segundos: depois da primeira inicialização, o contêiner precisa funcionar por pelo menos dez segundos para que a política de reinicialização seja ativada. Isso evita que um erro de configuração cause um loop infinito e consuma todos os recursos do sistema.
- Armadilha da reinicialização infinita: se o contêiner reiniciar continuamente por causa de um erro de configuração, como um conflito de porta, os logs poderão crescer de forma explosiva. Use também rotação de logs.
É possível alterar dinamicamente a política de um contêiner em execução:
docker update --restart=unless-stopped <container_id>
No docker-compose:
services:
web:
image: nginx
restart: unless-stopped # Recomendado para produção
worker:
image: my-worker
restart: on-failure # Pode falhar, mas não deve tentar infinitamente
Gerencie os logs para evitar que o disco fique cheio
Por padrão, o Docker armazena todos os logs dos contêineres em arquivos JSON. Com o tempo, esses arquivos podem consumir dezenas de gigabytes. Eu mesmo já enfrentei um incidente em que os logs do Docker lotaram o disco de um servidor de produção.
Configure a rotação de logs — recomendado:
Crie ou edite /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m", // Tamanho máximo de 10 MB por arquivo de log
"max-file": "3" // Mantém no máximo três arquivos
}
}
Reinicie o Docker depois da alteração:
sudo systemctl restart docker
Assim, os logs de cada contêiner ocuparão no máximo 30 MB — 10 MB × 3 — e os arquivos antigos serão excluídos automaticamente.
Configuração para um único contêiner:
docker run --log-opt max-size=10m --log-opt max-file=3 my-app
No docker-compose:
services:
app:
image: my-app
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Outras opções de driver de logs:
- syslog: envia os dados para o log do sistema.
- journald: usa o journal do systemd.
- fluentd: envia os dados ao Fluentd para gerenciamento centralizado.
- none: não registra logs — não recomendado.
Veja o caminho e o tamanho do arquivo de log do contêiner:
docker inspect --format='{{.LogPath}}' <container_id>
du -h $(docker inspect --format='{{.LogPath}}' <container_id>)
Monitoramento e alertas: descubra problemas com antecedência
Não espere o contêiner cair para perceber que há algo errado. O monitoramento preventivo pode evitar muitos incidentes de produção.
Monitoramento básico com docker stats:
docker stats # Mostra em tempo real o uso de recursos de todos os contêineres
docker stats <container_id> # Monitora um contêiner específico
Esse comando mostra em tempo real o uso de CPU, memória, E/S de rede e E/S de disco. Se o consumo de memória crescer continuamente, pode haver um vazamento que precisa ser corrigido antes de causar uma falha.
Em produção: Prometheus + Grafana
Uma abordagem mais profissional é coletar métricas com Prometheus e visualizá-las no Grafana:
# docker-compose.yml
services:
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3000:3000"
cadvisor: # Coleta métricas dos contêineres
image: google/cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "8080:8080"
Configure regras de alerta para enviar notificações automaticamente quando, por exemplo, o uso de memória passar de 80% ou um contêiner reiniciar vezes demais.
Uma alternativa simples: script agendado
Se Prometheus parecer complexo demais, use um script simples:
#!/bin/bash
# check-containers.sh
# Verifica se há contêineres com status Exited
EXITED=$(docker ps -a -f "status=exited" --format "{{.Names}}")
if [ -n "$EXITED" ]; then
echo "Warning: The following containers are exited:"
echo "$EXITED"
# Aqui você pode enviar um e-mail ou uma notificação
fi
Adicione-o ao crontab para executar a cada cinco minutos:
*/5 * * * * /path/to/check-containers.sh
Checklist de configuração para produção
Por fim, aqui está um checklist de configuração para produção. Com esses ajustes, você reduz bastante a chance de problemas:
version: '3.8'
services:
web:
image: my-web-app:latest
# Política de reinicialização
restart: unless-stopped
# Limites de recursos
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
memory: 512M
# Verificação de integridade
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 40s
# Gerenciamento de logs
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# Variáveis de ambiente (use secrets para dados confidenciais)
environment:
- NODE_ENV=production
# Mapeamento de portas
ports:
- "8080:8080"
# Dependências
depends_on:
database:
condition: service_healthy
database:
image: postgres:14
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
volumes:
- db-data:/var/lib/postgresql/data
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
volumes:
db-data:
Com essa configuração, contêineres que falharem reiniciarão automaticamente, o uso excessivo de recursos será limitado, os logs não lotarão o disco e o monitoramento poderá alertar você com antecedência. Assim fica mais fácil dormir tranquilo.
Conclusão
Depois de tudo isso, espero que uma ideia fique clara: a falha na inicialização de um contêiner Docker não é assustadora. O verdadeiro problema é não ter um método sistemático de diagnóstico.
Vamos recapitular os pontos principais deste artigo:
Entenda os códigos de saída: ao ver 137, pense em memória; ao ver 1, pense em configuração ou dependências. O código de saída é uma pista deixada pelo Docker, então não o ignore.
Diagnóstico em quatro etapas:
- Verifique o status do contêiner com
docker ps -a. - Consulte os logs com
docker logs. - Confira a configuração com
docker inspect. - Faça uma validação interativa com
docker run -it.
Mais de 90% dos problemas podem ser resolvidos na segunda etapa.
Cinco cenários comuns: erro de configuração, falta de memória, conflito de porta, problemas de permissão e dependências ainda não prontas. Memorizar como diagnosticar esses casos já resolve a maior parte das situações.
Previna-se: configure verificações de integridade, defina políticas de reinicialização, gerencie os logs e monitore os serviços. Esses mecanismos tornam os contêineres mais estáveis e permitem recuperação automática quando algo falhar.
Por fim, aqui está um checklist rápido de diagnóstico para você salvar:
Checklist para falha na inicialização de contêiner Docker
□ Etapa 1: docker ps -a para ver o status e o código de saída
□ Etapa 2: docker logs <container_id> para consultar os logs detalhados
□ Etapa 3: docker inspect <container_id> para verificar a configuração
□ Etapa 4: docker run -it <image> para fazer uma validação interativa
Identificação rápida de problemas comuns:
- Exit Code 1 + "No such file" → verifique os caminhos de montagem e os arquivos de configuração
- Exit Code 1 + "port already allocated" → verifique conflitos de porta
- Exit Code 1 + "Permission denied" → verifique as permissões dos arquivos e o SELinux
- Exit Code 1 + "Connection refused" → verifique se os serviços dependentes estão prontos
- Exit Code 137 + OOMKilled=true → aumente o limite de memória
- Exit Code 127 → verifique se o caminho de CMD/ENTRYPOINT está correto
Medidas preventivas:
□ Configure HEALTHCHECK
□ Defina uma política restart (unless-stopped é a recomendação)
□ Configure a rotação de logs (max-size + max-file)
□ Defina limites de recursos (-m para memória)
□ Configure monitoramento e alertas (docker stats ou Prometheus)
Se este artigo ajudou, salve-o para consultar na próxima vez que enfrentar um problema de inicialização. Se você já encontrou alguma falha incomum em um contêiner, compartilhe sua experiência nos comentários: ela pode ajudar outras pessoas.
Que seus contêineres permaneçam sempre Up and Running e que você nunca mais receba um alerta de contêiner fora do ar em uma sexta-feira à noite!
Processo completo para diagnosticar falhas na inicialização de contêineres Docker
Método sistemático de diagnóstico que explica os códigos de saída 137 e 1, um processo em quatro etapas e soluções para cinco cenários comuns de falha
⏱️ Estimated time: 30 min
- 1
Step 1: Entenda os códigos de saída e a gravidade do problema
Significado dos códigos de saída:
• Exit Code 0: encerramento normal, tarefa concluída
• Exit Code 1: erro da aplicação, falha no comando de inicialização (o mais comum)
• Exit Code 137: processo encerrado pelo OOM Killer, memória insuficiente
• Exit Code 127: comando não encontrado
• Exit Code 139: falha de segmentação (programas em C/C++)
Gravidade do problema:
• Os contêineres dos quatro serviços principais em produção ficaram com status Exited
• Contêineres que encerram imediatamente após iniciar exigem um método sistemático de diagnóstico
Identificação rápida de problemas comuns:
• Exit Code 1 + 'No such file' → verifique os caminhos de montagem e os arquivos de configuração
• Exit Code 1 + 'port already allocated' → verifique conflitos de porta
• Exit Code 1 + 'Permission denied' → verifique as permissões dos arquivos e o SELinux
• Exit Code 1 + 'Connection refused' → verifique se os serviços dependentes estão prontos
• Exit Code 137 + OOMKilled=true → aumente o limite de memória - 2
Step 2: Aplique o diagnóstico em quatro etapas
Diagnóstico em quatro etapas:
Etapa 1: verifique o status e o código de saída do contêiner
• Use docker ps -a para ver o status e o código de saída
• Observe as colunas State e Exit Code
Etapa 2: consulte os logs detalhados
• Use docker logs <container_id> para consultar os logs detalhados
• Use docker logs --tail 50 <container_id> para ver as 50 linhas mais recentes
• Use docker logs -f <container_id> para acompanhar os logs em tempo real
Etapa 3: confira a configuração
• Use docker inspect <container_id> para verificar a configuração
• Examine campos como Cmd, Entrypoint e Env
Etapa 4: faça uma validação interativa
• Use docker run -it <image> para validar interativamente
• Execute o comando de inicialização manualmente e observe a mensagem de erro - 3
Step 3: Resolva os cinco cenários comuns de falha
Cinco cenários comuns de falha:
Cenário 1: comando de inicialização incorreto
• Configuração incorreta de CMD/ENTRYPOINT
• Solução: corrija o comando de inicialização e verifique caminhos e parâmetros
Cenário 2: erro no arquivo de configuração
• Caminho ou formato incorreto
• Solução: corrija o arquivo de configuração e valide o caminho e o formato
Cenário 3: serviço dependente ainda não está pronto
• O banco de dados não iniciou
• Solução: aguarde o serviço dependente ficar pronto (use depends_on + healthcheck)
Cenário 4: memória insuficiente
• O OOM Killer encerrou o processo
• Solução: aumente o limite de memória (--memory) ou otimize o uso de memória da aplicação
Cenário 5: conflito de porta
• A porta já está em uso
• Solução: altere o mapeamento de porta (-p 8081:80) ou interrompa o processo que ocupa a porta
Boas práticas:
• Configure verificações de integridade no docker-compose
• Use depends_on + healthcheck para garantir que os serviços dependentes estejam prontos
• Defina limites adequados de recursos (memória e CPU)
FAQ
Por que um contêiner Docker encerra imediatamente após iniciar?
Significado dos códigos de saída:
• Exit Code 1: erro da aplicação, falha no comando de inicialização (o mais comum)
• Exit Code 137: processo encerrado pelo OOM Killer, memória insuficiente
• Exit Code 0: encerramento normal, tarefa concluída
Identificação rápida de problemas comuns:
• Exit Code 1 + 'No such file' → verifique os caminhos de montagem e os arquivos de configuração
• Exit Code 1 + 'port already allocated' → verifique conflitos de porta
• Exit Code 1 + 'Permission denied' → verifique as permissões dos arquivos e o SELinux
• Exit Code 1 + 'Connection refused' → verifique se os serviços dependentes estão prontos
• Exit Code 137 + OOMKilled=true → aumente o limite de memória
Método de diagnóstico: siga as quatro etapas (consultar os logs → verificar o código de saída → conferir o comando de inicialização → verificar os limites de recursos)
Como diagnosticar uma falha na inicialização de um contêiner Docker?
1) Consulte os logs do contêiner: docker logs container-name
2) Verifique o código de saída: docker ps -a
3) Confira o comando de inicialização: docker inspect container-name
4) Verifique os limites de recursos: docker stats
Passos detalhados:
• Etapa 1: use docker ps -a para ver o status e o código de saída do contêiner
• Etapa 2: use docker logs <container_id> para consultar os logs detalhados
• Etapa 3: use docker inspect <container_id> para verificar a configuração
• Etapa 4: use docker run -it <image> para fazer uma validação interativa
Qual é a diferença entre Exit Code 1 e Exit Code 137?
• Exit Code 1: erro da aplicação, falha no comando de inicialização
• Exit Code 137: processo encerrado pelo OOM Killer, memória insuficiente
• Exit Code 0: encerramento normal
• Outros códigos de saída: dependem da aplicação
Causas comuns do Exit Code 1:
• Comando de inicialização incorreto (configuração incorreta de CMD/ENTRYPOINT)
• Erro no arquivo de configuração (caminho ou formato incorreto)
• Serviço dependente ainda não está pronto (o banco de dados não iniciou)
• Conflito de porta (a porta já está em uso)
Causas comuns do Exit Code 137:
• Memória insuficiente (OOM Killer)
• É necessário aumentar o limite de memória (--memory)
Como resolver os problemas mais comuns de inicialização de contêineres?
1) Comando de inicialização incorreto (configuração incorreta de CMD/ENTRYPOINT)
2) Erro no arquivo de configuração (caminho ou formato incorreto)
3) Serviço dependente ainda não está pronto (o banco de dados não iniciou)
4) Memória insuficiente (OOM Killer)
5) Conflito de porta (a porta já está em uso)
Soluções:
• Corrija o comando de inicialização
• Corrija o arquivo de configuração
• Aguarde o serviço dependente ficar pronto (use depends_on + healthcheck)
• Aumente o limite de memória (--memory)
• Altere o mapeamento de porta (-p 8081:80)
• Configure verificações de integridade no docker-compose
23 min de leitura · Publicado em: 18 dez 2025 · Atualizado em: 4 set 2026
Guia prático Docker
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Guia para depurar contêineres Docker: como usar docker exec para investigar problemas
Aprenda a usar docker exec corretamente para entrar em contêineres e depurar problemas, incluindo diferenças entre exec e attach, instalação de ferramentas, definição de usuários e exemplos completos de comandos.
Parte 32 de 34
Próximo
Dev Sandbox self-hosted com Docker e Go: preview URLs sem Kubernetes
Guia prático para criar Dev Sandboxes self-hosted com Docker, Go e Traefik: preview URLs, limites de recursos, segurança e operação.
Parte 34 de 34



Comentários
Entre com GitHub para comentar