Skip to main content
DevOps Insightsguides & answers

Docker · troubleshooting

Container exits immediately

The container starts, the process finishes, and Docker stops the container — because a container lives exactly as long as its main process. The exit code tells you which of two quite different situations you are in.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 10 sections

Before you start

  • A terminal on the Docker host, or on the machine running Docker Desktop, as a user who can run docker commands. On Linux that means root or the docker group, which Docker's documentation says grants root-level privileges.
  • The stopped container itself, not only the image. A container started with --rm is removed when it exits, logs included, so run it again without --rm before you investigate.
  • The image's Dockerfile, or docker image inspect output, so you can see the ENTRYPOINT and CMD it was built with.
  • The application's own list of required environment variables and config files, to compare with what the container was given.
  • For an architecture check, your host's CPU architecture (uname -m) and, if you need to rebuild, a builder that supports docker build --platform.

Run this first

step 1 of 3
docker ps -a --filter name=<container> --format '{{.Status}}'

Look for Exited (0) means nothing failed — the process finished. Any non-zero code means it tried and could not.

The exit code splits this into two different investigations, so it is the first thing to establish.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

What exit code does docker ps -a report?

This is the branch point — 0 and non-zero lead to almost unrelated causes.

Cause space

5 of 5 still possible

  • The main process was never going to stay runningCommon
  • Required configuration was absent, so the process exited on start-upCommon
  • The image was built for a different CPU architectureOccasional
  • ENTRYPOINT and CMD combined into something that is not a commandOccasional
  • The container was killed for exceeding its memory limit during start-upRare

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Check it with a tool

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    docker ps -a --filter name=<container> --format '{{.Status}}'

    The exit code splits this into two different investigations, so it is the first thing to establish.

    Look for Exited (0) means nothing failed — the process finished. Any non-zero code means it tried and could not.

  2. Step 2

    docker logs <container> 2>&1 | tail -40

    For a non-zero exit, the application usually said why. For exit 0, the emptiness is itself the answer.

    Look for A named missing variable, a connection refusal, or exec format error.

  3. Step 3

    docker inspect <container> --format 'code={{.State.ExitCode}} oom={{.State.OOMKilled}} cmd={{json .Config.Cmd}} ep={{json .Config.Entrypoint}}'

    Everything else in one line: the code, whether the kernel killed it, and what it was actually asked to run.

    Look for An entrypoint and command that concatenate into something sensible, and oom=false.

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

The main process was never going to stay running

Common

The command completed and returned, so the container had nothing left to do. Classic versions: a base image whose default command is a shell that immediately reaches end-of-input, a script that ends, or a server launched with a trailing & so the foreground shell exits while the server is still starting.

Confirm

docker inspect <container> --format '{{.State.ExitCode}} {{json .Config.Cmd}} {{json .Config.Entrypoint}}'

Exit code 0 alongside a command that is not a long-running server. If Entrypoint is null and Cmd is something like ["bash"], that is the whole answer.

Fix

  • Run the actual server in the foreground as the container's main process. A container does not need a process manager; the application is the process.
  • If you genuinely need the container to stay up for debugging, run it interactively with -it so the shell has a terminal to read from.
  • Remove any trailing & from the start command. Backgrounding the server means the foreground command finishes, which ends the container.
# Foreground, in exec form
CMD ["node", "server.js"]

# Or, to keep a shell alive for poking around:
docker run -it --entrypoint sh myimage

Exec form also matters for signal handling — see ENTRYPOINT vs CMD.

Required configuration was absent, so the process exited on start-up

Common

The application read its configuration, found something missing — a database URL, an API key, a required environment variable — and exited rather than starting in an unusable state. This is correct behaviour and it looks like a crash.

Confirm

docker logs <container> 2>&1 | tail -30

An explicit message near the end naming what was missing. Most frameworks say so plainly; the difficulty is that people check docker ps rather than the logs.

Fix

  • Pass the missing values with -e or --env-file, or mount the config the application expects.
  • Check for a name mismatch rather than an absence — a variable spelled DATABASE_URL in compose and read as DB_URL in code produces exactly this.
  • Do not bake secrets into the image to make this go away. Inject them at run time.

The image was built for a different CPU architecture

Occasional

An image built on an arm64 machine and run on amd64, or the reverse. The container is created and, unless the host can emulate the other architecture, the binary cannot execute, so it fails almost instantly with an exec-format error. Docker Desktop runs other architectures under emulation by default, so an image that works on a laptop can still fail this way on a Linux server without QEMU registered.

Confirm

docker logs <container> 2>&1 | head -5; docker image inspect <image> --format '{{.Architecture}}'

exec format error in the logs, or an image Architecture that differs from your host's.

Fix

  • Build for the target platform explicitly with docker build --platform linux/amd64.
  • For images used across both, build a multi-platform image rather than one per developer.

ENTRYPOINT and CMD combined into something that is not a command

Occasional

With an exec-form ENTRYPOINT, Docker appends CMD to it as arguments. A shell-form ENTRYPOINT ignores CMD entirely. When one is set as a full command and the other adds unexpected arguments, the result can be a valid string that is not a valid invocation — so the process fails immediately with a usage error or a not-found.

Confirm

docker inspect <image> --format 'entrypoint={{json .Config.Entrypoint}} cmd={{json .Config.Cmd}}'

Read the two as one line concatenated in that order. If the combined result is not something you would type, that is the fault.

Fix

  • Put the executable in ENTRYPOINT and only its default arguments in CMD.
  • Use exec form — the bracket syntax — for both. Shell form introduces /bin/sh -c, which changes both parsing and signal handling.

The container was killed for exceeding its memory limit during start-up

Rare

A memory limit lower than the application's start-up footprint. The kernel kills the process before it finishes initialising, so it looks like an immediate exit rather than a resource problem.

Confirm

docker inspect <container> --format '{{.State.ExitCode}} oom={{.State.OOMKilled}}'

oom=true, or exit code 137. Note that a 137 with oom=false means something sent SIGKILL for another reason.

Fix

  • Raise the limit above the observed start-up peak, which is often higher than steady-state usage.
  • Check that the runtime's heap ceiling sits below the container limit. Current JVMs detect the limit automatically, but an explicit -Xmx above it, or Node's --max-old-space-size, lets the process exceed it.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

A container is not a small computer that stays switched on. It runs one main program, and when that program ends, the container stops with it. This page helps you work out whether that program finished normally or gave up, using the exit code Docker records when it stops.

What the exit code tells you, from Docker's documentation

Documented, not reproduced: Docker is not installed on this machine. The output is quoted from the Exit status section of Docker's Running containers page, which runs three busybox containers in the foreground and prints the exit code of docker run with echo $?.

Command

docker run busybox /etc; echo $?
docker run busybox foo; echo $?
docker run busybox /bin/sh -c 'exit 3'
echo $?

Output · as shown in Docker — Running containers: exit status

$ docker run busybox /etc; echo $?

docker: Error response from daemon: Container command '/etc' could not be invoked.
126

$ docker run busybox foo; echo $?

docker: Error response from daemon: Container command 'foo' not found or does not exist.
127

$ docker run busybox /bin/sh -c 'exit 3'
$ echo $?
3

How to read it

  • 126: Docker found /etc but could not invoke it, because it is a directory, not a program. The fault is in what the container was told to run, so look at ENTRYPOINT, CMD and your docker run arguments, not at the application.
  • 127: the command does not exist in the image. Check the spelling, the path, and that the image actually contains the program you named.
  • 3: Docker's documentation says any code other than 125, 126 and 127 is the exit code of the container's own command. The application chose it, so docker logs is where to look next. Exit code 0 is the same case with nothing to report: the program simply finished.
  • 125, not shown here, means the error is with Docker itself, such as an unknown flag, and the container never ran.
  • These examples run in the foreground. For a container started with -d, the same number appears in the STATUS column of docker ps -a, for example Exited (137) in Docker's docker ps reference, and in docker inspect --format '{{.State.ExitCode}}' <container>.

Words used on this page

Main process
The program named by the image's ENTRYPOINT and CMD, or by the command you add after the image name in docker run. Docker's documentation calls it the container's main running process; the container stops when it ends.
Exit code
The number the main process returns when it ends. By convention 0 means success. Docker uses 125, 126 and 127 for its own failures to start the command; any other number comes from your program.
ENTRYPOINT and CMD
The two Dockerfile instructions that decide what runs. Arguments from an exec-form CMD, or from docker run <image> ..., are appended to an exec-form ENTRYPOINT. A shell-form ENTRYPOINT ignores them.
Exec form and shell form
Exec form is the JSON array syntax, CMD ["node", "server.js"], which runs the program directly. Shell form, CMD node server.js, wraps it in /bin/sh -c, so the program is not PID 1 and does not receive the SIGTERM that docker stop sends.
OOMKilled
A field in docker inspect output under .State. It is true when the kernel's out-of-memory killer ended the container's process because it went over its memory limit.
exec format error
The kernel refusing to run a file. Linux's execve(2) manual describes ENOEXEC as an executable that is not in a recognised format or is for the wrong architecture.
Emulation
Running an image built for another CPU architecture through QEMU. Docker Desktop does this by default, so an architecture mismatch can work on a laptop and fail on a server.
What is actually happening

A container is not a machine that stays up. It is a process, and when that process returns, there is nothing left to run. Docker is not stopping your container; your container finished.

So the useful question is never "why did Docker stop it". It is "what did PID 1 do, and why did it return" — and the exit code splits that into two almost unrelated investigations.

Exit code 0 means the process completed successfully and had nothing more to do. That is usually a command that was never going to stay running: a shell with no input, a one-shot script, a server started in the background so the foreground command returned. Nothing failed.

A non-zero exit code means the process tried to run and could not. Now the logs matter, because the application had something to say before it gave up.

How to tell this is your problem
WhereWhat you see
docker psThe container is absent. It never appears, because it is no longer running.
docker ps -aSTATUS reads Exited (0) or Exited (1) a few seconds after creation.
docker logs <container>Either empty (exit 0, nothing to say) or the application's own error output.

How to know it is actually fixed

  • docker ps shows the container running rather than absent, and it is still there a minute later.
  • docker logs shows the application's normal start-up output reaching a listening or ready state.
  • The container survives a docker restart, which rules out a first-run-only condition.

Stopping it happening again

  • Add a HEALTHCHECK that exercises the application rather than the port, so a container that starts but cannot serve is distinguishable from one that works.
  • Fail fast and loudly on missing configuration, naming the variable. An application that exits silently converts a one-minute fix into a debugging session.
  • Use exec form for ENTRYPOINT and CMD, which avoids both the concatenation surprises and the signal-handling problem.

Security considerations

  • Do not make a missing-configuration exit go away by baking secrets into the image. Docker's Dockerfile reference warns that build arguments are visible in docker history, and values set with ENV persist into every container and can be read with docker inspect.
  • Values you pass at run time with -e or --env-file are stored in the container's configuration as .Config.Env, so anyone who can run docker inspect on that host can read them. Where the application supports it, mount secrets as files instead of passing them as variables.
  • The diagnose commands need access to the Docker daemon, which is root-equivalent on Linux. Use existing admin access rather than adding people to the docker group just to debug one container.
  • Do not work around an out-of-memory exit with --oom-kill-disable alone. Docker's documentation says to use it only together with -m; without a limit the host can run out of memory and the kernel may kill host processes instead.
  • A container that rejected its configuration may print that configuration in its logs, connection strings and tokens included. Read docker logs output before you paste it into a ticket or chat.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Terms used here (1)

Short definitions for the vocabulary on this page, in case any of it is new. Each links to the full entry.

Docker
A platform for building, shipping, and running applications inside containers. Docker uses OS-level virtualization to deliver software in portable, reproducible packages that work identically across development, staging, and production.

All 59 terms in the glossary

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it