Alternar tema

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

Easton editorial illustration: bottleneck pressure gauge

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:

  1. O contêiner ultrapassou a memória disponível, e o OOM Killer do kernel Linux encerrou o processo.
  2. Alguém — ou o sistema — executou docker kill ou kill -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 --help verifica 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 database e telnet 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 found ou syntax 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:

  1. Use docker inspect --format '{{.Mounts}}' para verificar os caminhos de montagem.
  2. Entre no contêiner e use ls para confirmar se o arquivo realmente está no local esperado.
  3. 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 yamllint para verificar a sintaxe.
  • Para arquivos JSON, valide com jq: jq . config.json.
  • Para a configuração do MySQL, execute mysqld --verbose --help no 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}}' retorna true.
  • Os logs podem mostrar mensagens como Cannot allocate memory ou Out 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-reservation como 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 use ou bind: 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 permitted ou chown: 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, ECONNREFUSED ou could 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_on do 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:

  1. 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.
  2. 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:

  1. Verifique o status do contêiner com docker ps -a.
  2. Consulte os logs com docker logs.
  3. Confira a configuração com docker inspect.
  4. 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. 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. 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. 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?
As causas mais comuns de um contêiner encerrar logo após iniciar são:

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?
Diagnóstico em quatro etapas:
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?
Significado dos códigos de saída:
• 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?
Cinco cenários comuns de falha:
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

Comentários

Entre com GitHub para comentar

Easton BlogEaston Blog