--:--:--

Docker container exited: como descobrir por que o contêiner encerra logo após iniciar

Publicado em 2026-08-29T20:13:00Z · atualizado em 2026-08-29T19:13:41+00:00

Você executa `docker run`, o terminal volta imediatamente e `docker ps` não mostra nada. Ao usar `docker ps -a`, o contêiner aparece como **Exited**. Isso não significa que o Docker “fechou sozinho”: um contêiner permanece ativo enquanto se

Relação entre docker run, processo principal PID 1 e estado Exited
Imagem de apoio ao tema do artigo.

Docker container exited: como descobrir por que o contêiner encerra logo após iniciar

Você executa docker run, o terminal volta imediatamente e docker ps não mostra nada. Ao usar docker ps -a, o contêiner aparece como Exited. Isso não significa que o Docker “fechou sozinho”: um contêiner permanece ativo enquanto seu processo principal está ativo. Se esse processo termina, por sucesso ou falha, o estado do contêiner passa a encerrado.

Este guia apresenta uma sequência de diagnóstico baseada em evidências: localizar o contêiner, ler logs, interpretar o código de saída, inspecionar comando e configuração, testar o processo em primeiro plano, validar variáveis, portas, volumes, permissões, arquitetura, limites e sinais. A política de reinício aparece somente depois da causa.

Relação entre docker run, processo principal PID 1 e estado Exited Legenda: a vida do contêiner acompanha o processo principal; quando o PID 1 termina, o contêiner termina.

Encontre o contêiner que sumiu do docker ps

docker ps lista apenas contêineres em execução. Inclua os encerrados:

docker ps -a
docker ps -a --filter status=exited
docker ps -a --latest

Anote CONTAINER ID, nome, imagem, status e comando. Se um sistema cria e remove contêineres com --rm, o objeto pode desaparecer ao terminar; durante o diagnóstico, retire essa opção em um ambiente de teste para preservar os metadados.

Formate uma visão objetiva:

docker ps -a --format 'table {{.ID}}\t{{.Names}}\t{{.Image}}\t{{.Status}}'

Não remova todos os contêineres ou volumes para “começar limpo”. Essa ação elimina evidências e pode apagar dados não persistidos. Trabalhe em uma cópia ou serviço de desenvolvimento.

Leia logs e código de saída primeiro

Os logs do contêiner mostram a saída padrão e de erro do processo:

docker logs nome-ou-id
docker logs --timestamps --tail 200 nome-ou-id

Depois inspecione estado:

docker inspect nome-ou-id --format '{{json .State}}'
docker inspect nome-ou-id --format 'exit={{.State.ExitCode}} erro={{.State.Error}} oom={{.State.OOMKilled}}'

Código 0 significa que o processo terminou com sucesso. Para um comando em lote, isso pode ser exatamente o esperado. Para servidor web, geralmente indica que o comando executou uma tarefa curta ou colocou o servidor em segundo plano. Códigos diferentes de zero indicam falha definida pelo programa ou pelo ambiente.

Os valores 126 e 127 frequentemente apontam, respectivamente, para comando não executável e comando não encontrado. 137 costuma corresponder a encerramento por SIGKILL, que pode envolver falta de memória ou ação externa; confirme .State.OOMKilled e eventos em vez de assumir. 143 costuma representar SIGTERM, comum em desligamento controlado.

Reproduza um encerramento normal

Este comando imprime uma mensagem e termina com zero:

docker run --name demo-exit alpine:latest sh -c 'echo tarefa concluida'
docker ps -a --filter name=demo-exit
docker inspect demo-exit --format '{{.State.ExitCode}}'

O estado Exited (0) é correto porque echo acabou. Compare com um processo duradouro:

docker run --name demo-http -d -p 8080:80 nginx:alpine
docker ps --filter name=demo-http
docker logs demo-http

Ao terminar, remova apenas os objetos do laboratório pelos comandos normais do Docker. Não use esse exercício em um host compartilhado sem autorização.

CMD, ENTRYPOINT e o comando real

A imagem define ENTRYPOINT e CMD; argumentos de docker run podem substituir parte da configuração. Veja o resultado:

docker inspect nome-ou-id --format 'entrypoint={{json .Config.Entrypoint}} cmd={{json .Config.Cmd}}'
docker image inspect nome-da-imagem --format 'entrypoint={{json .Config.Entrypoint}} cmd={{json .Config.Cmd}}'

Um erro comum é trocar o comando do servidor por um script de migração que termina. Outro é usar a forma shell e criar comportamento inesperado de sinais e expansão.

Forma executável recomendada no Dockerfile:

ENTRYPOINT ["python", "-m", "meu_app"]
CMD ["--host", "0.0.0.0", "--port", "8000"]

A forma JSON não passa automaticamente por um shell. Variáveis como $PORT não são expandidas da mesma maneira; se a aplicação precisa delas, leia-as dentro do programa ou use um script de entrada bem escrito que finalize com exec.

O servidor foi enviado para segundo plano

Serviços antigos podem usar a opção --daemon, iniciar um filho e encerrar o processo pai. Em uma máquina tradicional isso é útil; no contêiner, o serviço principal deve ficar em primeiro plano.

Exemplo conceitual:

# Evite: o comando retorna e o contêiner termina
CMD ["meu-servidor", "--daemon"]

# Prefira: processo principal em primeiro plano
CMD ["meu-servidor", "--foreground"]

Não mantenha o contêiner vivo com tail -f /dev/null como correção de produção. Isso mascara que o serviço real morreu e faz o orquestrador acreditar que existe algo útil em execução. Use esse recurso apenas em laboratório temporário, com propósito explícito.

Variáveis de ambiente e segredos ausentes

Aplicações encerram na inicialização quando falta URL do banco, chave, ambiente ou configuração. Compare os nomes esperados com os enviados:

docker inspect nome-ou-id --format '{{range .Config.Env}}{{println .}}{{end}}'

Esse comando pode exibir segredos. Não cole a saída em tickets ou logs públicos. Prefira uma verificação que mostre apenas presença:

docker run --rm --env-file .env imagem sh -c '
  test -n "$DATABASE_URL" && echo DATABASE_URL=presente || echo DATABASE_URL=ausente
'

Arquivos .env têm sintaxe própria e não são scripts shell completos. Espaços, aspas e quebras de linha podem virar parte do valor. Confirme a documentação da ferramenta que carrega o arquivo. Em Compose, use docker compose config com cautela, pois a saída resolvida pode incluir valores sensíveis.

Volumes, caminhos e permissões

Um volume montado sobre o diretório da aplicação pode esconder arquivos que existiam na imagem. Se a imagem contém /app/config.yml e você monta uma pasta vazia em /app, o arquivo deixa de aparecer dentro do contêiner.

Inspecione montagens:

docker inspect nome-ou-id --format '{{json .Mounts}}'

Teste a imagem sem o volume em ambiente isolado. Depois confira o caminho e permissões. Aplicações modernas frequentemente executam com usuário não root; o diretório do host precisa permitir leitura ou escrita conforme a necessidade.

Não “corrija” com permissão total recursiva. Descubra UID e GID do processo:

docker run --rm imagem id
docker image inspect imagem --format '{{.Config.User}}'

Ajuste propriedade e modo mínimos no ambiente controlado. Em Windows e macOS com Docker Desktop, compartilhamento de pastas e tradução de permissões também podem influenciar.

Árvore de diagnóstico: logs, comando, configuração, volume e recursos Legenda: cada evidência reduz o espaço de causas antes de qualquer política de reinício.

Porta ocupada não é a mesma coisa que processo encerrado

Se a publicação -p 8080:80 falha porque a porta do host está em uso, o daemon pode nem iniciar o contêiner corretamente. A mensagem aparece no comando e em .State.Error. Já uma aplicação pode iniciar dentro do contêiner e falhar ao abrir sua porta interna.

Confira mapeamentos:

docker port nome-ou-id
docker inspect nome-ou-id --format '{{json .NetworkSettings.Ports}}'

O processo deve escutar em 0.0.0.0 dentro do contêiner, não apenas em 127.0.0.1, se precisa receber tráfego pela rede do contêiner. Porém, escutar incorretamente geralmente deixa o processo vivo e causa falha de conexão; não confunda indisponibilidade com estado Exited.

Imagem e arquitetura incompatíveis

Uma imagem construída para arquitetura diferente pode falhar com “exec format error”. Confira:

docker info --format '{{.Architecture}}'
docker image inspect imagem --format '{{.Architecture}}/{{.Os}}'

Imagens multi-plataforma publicam manifestos para mais de uma arquitetura. Ao construir, use uma base compatível e ferramentas de build multi-plataforma quando realmente precisar. Emulação pode funcionar, mas traz diferenças de desempenho e compatibilidade.

Outro erro vem de scripts com final de linha incompatível ou shebang apontando para interpretador inexistente. Verifique se o arquivo é executável, usa LF quando necessário e referencia um caminho presente na imagem, como #!/bin/sh.

Memória, CPU e encerramento externo

Se .State.OOMKilled é verdadeiro, o processo ultrapassou a memória disponível e foi encerrado. Consulte os limites configurados:

docker inspect nome-ou-id --format 'memory={{.HostConfig.Memory}} cpu={{.HostConfig.NanoCpus}}'
docker stats --no-stream

Não aumente memória antes de medir. Uma carga legítima pode precisar de limite maior; um vazamento continuará crescendo. Colete métricas, reproduza com entrada controlada e ajuste aplicação e capacidade.

Também verifique quem solicitou parada. docker events --since 30m mostra eventos recentes do daemon. Sistemas de implantação, scripts de limpeza e verificações externas podem executar stop ou substituir o contêiner. Preserve horários com fuso e correlacione com logs do orquestrador.

Healthcheck não mantém o processo vivo

Um HEALTHCHECK avalia se o serviço está saudável enquanto o contêiner roda. Ele não substitui o processo principal. Um contêiner pode estar running (unhealthy) ou pode encerrar antes de qualquer verificação.

Inspecione:

docker inspect nome-ou-id --format '{{json .State.Health}}'
docker image inspect imagem --format '{{json .Config.Healthcheck}}'

O teste deve verificar o serviço real, ter timeout curto e não depender de ferramenta ausente na imagem. Evite uma verificação que sempre retorna zero; ela produz confiança falsa. Também não use healthcheck para iniciar processos ou corrigir dados.

Docker Compose: veja a configuração realmente aplicada

Em Compose, o arquivo base pode ser combinado com sobrescritas, perfis e variáveis. O comando final nem sempre é o que aparece no primeiro YAML. Inspecione a configuração resolvida em ambiente seguro:

docker compose config
docker compose ps -a
docker compose logs --timestamps --tail 200 nome-do-servico

command substitui o CMD da imagem; entrypoint substitui o ENTRYPOINT. Uma lista e uma string podem ter semânticas diferentes em relação ao shell. Compare o serviço resolvido com docker inspect do contêiner criado.

Tenha cuidado: docker compose config pode interpolar valores. Redija segredos antes de anexar a saída. Prefira docker compose config --services para listar serviços quando não precisar dos detalhes.

Dependência entre serviços também confunde o diagnóstico. depends_on ordena criação conforme a configuração, mas disponibilidade do banco pode exigir healthcheck e uma política de espera na aplicação. Um banco “iniciado” ainda pode não aceitar conexões. O cliente deve tratar falhas transitórias com limite, atraso e mensagem clara, não encerrar e reiniciar sem fim.

Use docker compose run --rm serviço comando-de-diagnostico apenas quando compreender volumes, rede e variáveis herdados. Ele cria outro contêiner e pode não reproduzir portas ou ciclo de vida idênticos ao serviço normal.

Sinais, PID 1 e desligamento correto

O processo PID 1 dentro do contêiner recebe sinais de parada e tem responsabilidades especiais. Scripts de entrada que iniciam um servidor sem exec podem manter o shell como PID 1; o sinal não chega ao filho como esperado, o Docker aguarda o timeout e força encerramento.

Um script de entrada simples deve validar configuração e substituir-se pelo processo final:

#!/bin/sh
set -eu
: "${DATABASE_URL:?DATABASE_URL ausente}"
exec python -m meu_app

exec faz o aplicativo ocupar o lugar do shell. A aplicação deve tratar SIGTERM, parar de aceitar trabalho, concluir o que for seguro dentro do prazo e sair. Não prenda o desligamento indefinidamente.

Teste:

docker stop --time 15 nome-ou-id
docker inspect nome-ou-id --format 'exit={{.State.ExitCode}} inicio={{.State.StartedAt}} fim={{.State.FinishedAt}}'

Se o processo precisa de um inicializador para coletar filhos, avalie --init ou a configuração equivalente, conforme a documentação da plataforma. Isso não corrige uma aplicação que ignora sinais; apenas ajuda na gestão de processos filhos.

Um serviço que inicia bem mas termina somente em implantações pode estar recebendo SIGTERM normalmente durante substituição. Correlacione eventos e código de saída antes de classificar o encerramento como defeito.

Política de reinício só depois do diagnóstico

O Docker oferece políticas como no, on-failure, always e unless-stopped. A documentação oficial explica seus comportamentos. Reiniciar pode recuperar uma falha transitória, mas cria um ciclo rápido quando configuração ou comando estão errados.

Veja a política atual:

docker inspect nome-ou-id --format '{{json .HostConfig.RestartPolicy}}'

Para um serviço, on-failure:5 limita tentativas; para uma tarefa em lote que termina com zero, reiniciar pode executá-la de novo indevidamente. Defina a política conforme a natureza do processo e configure alertas para reinícios repetidos.

Procedimento completo de investigação

Use esta ordem:

  1. docker ps -a para preservar identidade e status;
  2. docker logs --timestamps para a mensagem da aplicação;
  3. docker inspect para código, erro, OOM e comando;
  4. validação de variáveis sem expor valores;
  5. inspeção de volumes, usuário, arquitetura e limites;
  6. reprodução da imagem sem sobrescritas desnecessárias;
  7. correção da causa e novo contêiner imutável;
  8. política de reinício e healthcheck adequados;
  9. teste de parada e inicialização controladas.

Desenhe o fluxo no criador de fluxogramas do IATechNerds e registre os testes nas ferramentas. Para analisar inventários exportados, use o iatnGrid sem enviar a planilha a um servidor externo.

Checklist para encerrar o diagnóstico

  • contêiner encerrado foi localizado com docker ps -a;
  • logs e .State foram preservados;
  • código de saída foi interpretado com evidências;
  • processo principal roda em primeiro plano;
  • ENTRYPOINT e CMD correspondem ao serviço desejado;
  • variáveis obrigatórias existem sem exposição de segredos;
  • volumes não escondem arquivos e possuem permissões mínimas;
  • arquitetura e interpretador do script são compatíveis;
  • limites e OOMKilled foram verificados;
  • healthcheck mede o serviço real;
  • política de reinício não mascara falha permanente;
  • a imagem corrigida passou por inicialização e desligamento controlados.

Documentação primária

Nota de produção

Rascunho autoral orientado à busca “Docker container exited immediately”. Os laboratórios usam imagens públicas conhecidas e objetos descartáveis, sem comandos de limpeza ampla. O comportamento de logs, inspeção, processo principal e reinício foi baseado na documentação oficial do Docker. A revisão humana deve testar os exemplos em Docker Engine ou Docker Desktop suportado e ajustar a nomenclatura editorial antes de publicar.

Camada extra: como tomar uma decisão melhor neste cenário

A parte mais importante de Docker container exited: como descobrir por que o contêiner encerra logo após iniciar não é decorar um botão: é entender quais sinais confirmam o diagnóstico e quais sinais indicam que estamos resolvendo o problema errado.

Você executa `docker run`, o terminal volta imediatamente e `docker ps` não mostra nada. Ao usar `docker ps -a`, o contêiner aparece como **Exited**. Isso não significa que o Docker “fechou sozinho”: um contêiner permanece ativo enquanto se A ideia central pode ser testada com quatro perguntas: o que foi observado, qual hipótese explica o sintoma, qual mudança é reversível e qual evidência prova que funcionou. Esse encadeamento evita que uma coincidência seja confundida com causa e torna o procedimento repetível por outra pessoa.

Qualidade de IA precisa de um teste, não de uma impressão

Para Docker, contêiner, DevOps, logs, escolha três tarefas representativas: uma simples, uma ambígua e uma em que a resposta possa ser conferida em fonte primária. Compare utilidade, rastreabilidade, privacidade, velocidade e quantidade de correção humana necessária. Uma resposta elegante não compensa uma citação inexistente ou uma conclusão que não pode ser reproduzida.

Separe ainda o que pode sair do dispositivo do que deve permanecer local. Dados públicos toleram fluxos diferentes de documentos internos, contratos, informações pessoais ou bases de clientes. A escolha entre processamento local e nuvem deve ser feita pelo risco do conteúdo, não apenas pela conveniência da interface.

Grade de avaliação prática

CritérioPerguntaEvidência
CorreçãoA resposta bate com a fonte?Checagem independente
RastreabilidadeÉ possível localizar de onde veio?Citação ou trecho verificável
PrivacidadeQue dado sai do dispositivo?Política e configuração
EsforçoQuanto trabalho humano resta?Tempo de revisão

O ponto em que a IA deixa de ajudar

Se a tarefa exige precisão absoluta e a saída não pode ser validada, a automação deve ser limitada a apoio: localizar trechos, organizar opções ou preparar uma primeira versão. A decisão final precisa permanecer com alguém capaz de verificar a fonte e assumir a responsabilidade pelo resultado.

Checklist de fechamento

  • Registre o estado inicial antes de alterar qualquer coisa.
  • Faça uma mudança por rodada e anote o efeito.
  • Teste um caso normal, um caso-limite e o cenário que originalmente falhou.
  • Confirme que a solução continua válida depois de reiniciar, reabrir ou repetir o fluxo.
  • Guarde uma forma de voltar ao estado anterior quando a alteração for destrutiva.

Esse método acrescenta profundidade sem transformar o artigo em teoria abstrata: o leitor entende por que cada passo existe e como reconhecer quando a situação exige uma decisão diferente.

#Docker #contêiner #DevOps #logs #infraestrutura