Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Read a Docker startup failure: exit codes and loops

By updated

Improve this page

Logo Docker

You start a container and it does not run. Sometimes docker run stops straight away on an error. Sometimes the container shows up in docker ps, stops, then starts again without end. In both cases Docker tells you what happened, but not always where you look first. The container logs are sometimes empty.

This lesson shows where to read the information. The exit code, a number the program returns when it ends, tells you who failed: Docker itself or the program started inside the container. The container status tells you whether Docker is still trying to restart it. You then learn to stop a restart loop by fixing the problem, rather than by turning restarts off.

All the commands on this page target Docker Engine 29.9.2.

What you will learn

  • Read the codes 125, 126 and 127 returned by docker run, and say who produces them
  • Find the error of a container for which docker logs returns nothing, in .State.Error
  • Recognize the Restarting status and read the .RestartCount counter
  • Compare the four --restart policies: no, on-failure[:n], always, unless-stopped
  • Stop a loop by fixing the problem, and know when to stop restarts in order to examine the container

Prerequisites

You need Docker installed and running (installing Docker), the basic commands from running a container, and docker logs and docker container inspect --format, both introduced in the lesson inspect a running container. Container states and the codes 137 and 143 are explained in lifecycle, PID 1 and clean stop. This lesson does not go back over them.

What do exit codes 125, 126 and 127 mean?

When docker run fails, it returns an exit code, a number your shell keeps in the $? variable. Docker reserves three of them, and each one tells you where to look. The lesson running a container introduced the codes 125 and 127. This one adds 126.

CodeWhat happenedWhere to look
125Docker could not start the containerThe message printed by docker run
126The command exists in the image but cannot be executedThe permissions of the file being run
127The command does not exist in the imageThe name of the command and the content of the image

With the codes 126 and 127, Docker did create the container, but the program never started. It therefore wrote nothing, and docker logs stays empty. The three tests below trigger the codes 125, 127 and 126, in that order.

First test, with an option docker run does not know. Docker refuses the command without creating a container. The echo then prints the code returned to your shell.

Terminal window
docker run --option-inventee alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a true
echo "code = $?"
unknown flag: --option-inventee
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Run 'docker run --help' for more information
code = 125

The message starts with unknown flag, Docker reminds you how to use docker run, and the code is 125.

Second test, with a tool that is missing from the image. The debian:13-slim image is deliberately minimal and does not contain curl. Docker creates the container, then cannot find the command to run.

Terminal window
docker run --name sans-curl debian:13-slim@sha256:f6b08b09a05725f9431502d3b5714f5c4576e82c1a84fa1debe0c24a7e0c3ec4 curl --version
echo "code = $?"
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "curl": executable file not found in $PATH
Run 'docker run --help' for more information
code = 127

The message ends with executable file not found in $PATH. The system looked for curl in the directories listed in the $PATH variable and found it nowhere. The code is 127.

Third test, with a file that exists but is not a program. /etc/passwd is in the image, without execute permission.

Terminal window
docker run --name sans-droit alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a /etc/passwd
echo "code = $?"
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "/etc/passwd": permission denied
Run 'docker run --help' for more information
code = 126

This time the message says permission denied and the code is 126. The file exists, but the system refuses to execute it.

The code 125 has a special case. If the port you publish is already taken by another container, Docker creates the container but cannot start it. docker ps -a then shows it in the created state, like the containers in the next section. The lesson publish a port explains this conflict.

For the codes 127 and 126, the docker run message already names the cause. The code is nevertheless enough to know what to do, without reading the whole message. With 127, install the tool or fix its name. With 126, make the file executable.

Where is the error when the logs are empty?

When the program never started, the error is not in the logs. It is in the container's .State.Error field. The logs only contain what the program wrote, and a program that did not start wrote nothing. Docker, for its part, noted the reason for the failure in the information it keeps about the container, and docker container inspect displays it.

The two commands below query the sans-curl container created above. The first reads the logs. The second reads the status, the code and the error.

Terminal window
docker logs sans-curl
docker container inspect sans-curl --format 'statut={{.State.Status}} code={{.State.ExitCode}} erreur={{.State.Error}}'

The first command prints nothing. The second prints Docker's message:

statut=created code=127 erreur=failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "curl": executable file not found in $PATH

Look at the status first. It is created, not exited: the container was created, but it never started. A container that ran and then stopped would be exited. The sans-droit container gives the same result, with the code 126 and the message permission denied.

The code 125 reads differently in two cases. With an unknown option, Docker creates no container. docker ps -a has nothing to list, and only the docker run message explains the failure. With a port conflict, Docker creates a container in the created state, whose .State.ExitCode is 128. The 125 was the code of the docker run command, not the container's.

The code 127 can also come from elsewhere. If you start a shell and that shell calls the missing tool, the shell returns 127, after it has started. It writes its message to the logs.

Terminal window
docker run --name via-shell alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sh -c 'outil-absent'
echo "code = $?"
docker logs via-shell
sh: outil-absent: not found
code = 127
sh: outil-absent: not found

A code 127 therefore has two possible origins. If the logs are empty, Docker did not find the command, and the message is in .State.Error. If the logs hold the message, an already running program did not find another command.

What does the Restarting status mean?

The Restarting status describes a container that Docker restarts again and again, because its program fails on every attempt and its restart policy asks for it. The policy is a setting you apply at creation, with the --restart option. It repairs nothing, it repeats. The container is therefore never really running: its program stops, Docker restarts it, and it starts over.

Start a container whose program prints one line and then always exits with the code 1. The always policy asks Docker to restart it.

Terminal window
docker run -d --name tourne-en-rond --restart always alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sh -c 'echo un essai de plus; exit 1'
sleep 2
docker ps -a --filter name=tourne-en-rond --format '{{.Names}} {{.Status}}'
4623f3a7b80f8c6dc7f7eca65476d41da0beacd3862ddfeb1076346206db1e95
tourne-en-rond Restarting (1) Less than a second ago

The status is Restarting, and the code of the last exit, 1, is in parentheses. The sleep 2 gives the program time to fail a first time. Without it, you would still see Up Less than a second. The identifier on the first line changes at every run.

Now query the container. You read its status, the code of its last exit, its policy and the number of restarts already made (labelled relances in the command), then its logs.

Terminal window
docker container inspect tourne-en-rond --format 'statut={{.State.Status}} code={{.State.ExitCode}} politique={{.HostConfig.RestartPolicy.Name}} relances={{.RestartCount}}'
docker logs tourne-en-rond
statut=running code=0 politique=always relances=4
un essai de plus
un essai de plus
un essai de plus
un essai de plus
un essai de plus

The .RestartCount field, displayed here under the name relances, counts the restarts made by Docker. Read it twice, a few seconds apart. If it goes up, the container is in a loop. docker logs repeats the same line on every attempt. Here there are five, the first start and four restarts, so every attempt fails the same way.

The status shown here is running with the code 0, which can be surprising. The inspection landed just after a restart: the program had just started again and its code had been reset to zero. Run the command again and you will most often read restarting and code=1. The restart counter, for its part, is reliable at any moment.

Docker pauses between two restarts. This pause doubles at every failure, from a fraction of a second at first up to one minute at most. On this container, the gap between two restarts went from 26 s to 51 s, then to 60 s. The loop therefore slows down on its own, but it does not stop.

What does each restart policy decide?

A policy decides in which cases Docker restarts the container. It says nothing about the health of the program. The --restart option accepts four values. The table shows, for each one, when it restarts, and what it does when Docker itself restarts.

PolicyRestarts when the program exitsAfter a Docker restart
no (default)neverthe container stays stopped
on-failure[:n]only if the code is not 0, at most n timesrestarted if its last exit was an error and the cap is not reached; stays stopped after an exit with the code 0
alwaysalways, even with the code 0restarted, even if it had been stopped by hand
unless-stoppedalways, even with the code 0restarted, unless it had been stopped by hand

In this table, on-failure:n is the only policy that gives up. After n attempts, Docker stops restarting the container. It stays stopped (exited) with its last code. Written without n, the policy never gives up.

always and unless-stopped both restart the container endlessly. They differ only when Docker restarts, for example when the machine reboots. A container you had stopped with docker stop comes back under always and stays stopped under unless-stopped. These behaviors were verified on Docker Engine 29.9.2, by restarting the service with systemctl restart docker.

Under on-failure, when Docker restarts, everything depends on the code the program stopped with. When you stop the container with docker stop or docker kill, the policy never restarts it on the spot, even with an error code. But when Docker restarts, it reads the last recorded code. A program that had stopped cleanly returned the code 0, and Docker leaves it stopped. A program that ignored the stop request was killed after the grace period with the code 137: when Docker restarts, it comes back. The lesson lifecycle, PID 1 and clean stop explains this code.

To see Docker give up, start a container whose program exits with the code 4, under the on-failure:3 policy.

Terminal window
docker run -d --name limite-trois --restart on-failure:3 alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sh -c 'exit 4'
sleep 5
docker container inspect limite-trois --format 'statut={{.State.Status}} code={{.State.ExitCode}} relances={{.RestartCount}} plafond={{.HostConfig.RestartPolicy.MaximumRetryCount}}'
5f191dc84d5697602ad06a68f7f47eb4fd66f095e0b87ec1cc100165507b5e55
statut=exited code=4 relances=3 plafond=3

relances is 3, like plafond (the cap): Docker made its three attempts, then it gave up. The status is exited, whereas tourne-en-rond stayed in Restarting. The abandoned container keeps its exit code and its counter, and you can still read them. If you start it again or recreate it, they start from zero.

How do you stop a restart loop?

To stop a loop, fix the problem that makes the program fail. Turning restarts off, or deleting the container, stops the loop but repairs nothing. The program was already failing on the first attempt, the fault remains entire, and you risk losing the information that would have let you find it. Three actions are possible, and they do not have the same scope. The first repairs, the second stops restarts, the third recreates the container.

To repair, read the error in docker logs or in .State.Error, then fix the problem outside the container. It may be a file to create, a missing piece of data, or a service that is not listening yet. Docker keeps restarting the container, and the next attempt succeeds.

The following example uses a named volume, a storage space that Docker manages separately from the container. It is empty at first. The program requires a file pret in this volume and fails as long as the file is missing. When the file is there, exec replaces the shell with sleep infinity, which waits forever and becomes the container's main process.

Terminal window
docker run -d --name patient --restart unless-stopped -v attente:/donnees alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sh -c 'test -f /donnees/pret && exec sleep infinity; echo fichier pret absent; exit 1'
docker logs patient
dbd7673f5a1921ee61fbfc93c782c09457bd3236c2d2eb5fa8c40d286bf57e52
fichier pret absent

The logs point at the missing file. You create it in the volume with a throwaway container, which the --rm option deletes as soon as it has finished, without touching patient.

Terminal window
docker run --rm -v attente:/donnees alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a touch /donnees/pret
docker ps --filter name=patient --format '{{.Names}} {{.Status}}'
patient Restarting (1) 6 seconds ago

The container is still in Restarting, because Docker has not yet made its next attempt. Do not restart patient by hand, let Docker do it. The delay between two attempts grows, so the wait can reach about a minute. Run docker ps again from time to time, until you see the Up status. In our test, the file was created about twenty seconds after the start, and the container went to Up six seconds after the file was created:

patient Up Less than a second

Docker restarted the container automatically once the problem was fixed. If you had restarted it yourself, you would only have skipped the wait, proving nothing more.

To stop the loop without repairing anything, docker update --restart=no removes the restart policy from a container that already exists, then docker stop stops it. You do this to examine the container while it is stopped. This is not a repair, the problem is still to be fixed.

Terminal window
docker update --restart=no tourne-en-rond
docker stop tourne-en-rond
docker ps -a --filter name=tourne-en-rond --format '{{.Names}} {{.Status}}'
tourne-en-rond
tourne-en-rond
tourne-en-rond Exited (1) Less than a second ago

The first two lines are the names returned by docker update then docker stop. The third line confirms that the container is stopped and that Docker no longer restarts it.

When the problem comes from the command or from a setting applied when the container was created, the third resort is to recreate it. You cannot change on an existing container its command, its -e variables, its -v mounts, its -p ports or its image, and docker update does not change them. Delete the container with docker rm, then recreate it with a corrected docker run. Recreating is the right answer in these cases, and only after reading why the container was failing.

How do you diagnose a container that will not start or restarts in a loop?

To find the origin of the problem, look at three things in this order: the container status, its exit code, then the place where the error is written. The diagram below follows these three steps.

First type docker ps -a. If the container does not appear, docker run did not create it, and only its message explains the failure. If it is in Restarting, Docker restarts it again and again. Read its policy and its restart counter, then fix the problem. If it is in Created, the program never started: read .State.Error, whatever code is displayed. If it is in Exited, look at its last exit code, which tells you where to look for the error. This is also the case when Docker gave up at the on-failure cap.

Docker never gives the code 125 to a container: it is the code of the docker run command when it fails itself. An Exited (125) container can nevertheless exist, if its program chose to exit with that code. In that case, .State.Error is empty and the 125 reads like any "other code": the explanation is in docker logs.

Decision tree: with no container (code 125 from docker run), read the docker run message; a Created container never started, so read .State.Error; a container restarted in a loop points to the restart policy; an Exited container, or one stopped at the on-failure cap, is read by its last code. For an Exited container, the codes 126, 127 and any other code, including a 125 chosen by the program, are read in docker logs, whereas 137 or 143 point to the lifecycle and signals lesson. A Created container, for its part, is read in .State.ErrorThe container does not startor restarts in a loopStatusin docker ps -a?Restarting:Docker relaunchesRestartingon-failure cap reached:Docker gave upExited, RestartCountat the capExit code?Exited125: code returned bydocker run, not bythe containerno container(option rejected)Read .State.Error(empty logs)Created:the program neverstartedRead the policyand RestartCountRead docker logs,fix the problem126: file found,not executable126127: commandnot found127137 or 143:a signal killed the process137, 143Other code:the application failedotherRead the docker runmessageRead docker logsSee the lesson onthe container lifecycle
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

For the codes 137 and 143, the diagram sends you to the lesson lifecycle, PID 1 and clean stop. These codes signal a process killed by a signal, and that lesson explains how to read them.

Troubleshooting: the common errors

The table links a symptom you observe to its probable cause, then to the solution.

SymptomCauseSolution
docker logs returns nothing and the container is in the created stateThe program never started (code 126, 127, or 128 on a port conflict)Read .State.Error with docker container inspect
Code 127 although the tool exists on the hostThe tool is missing from the image, often a minimal imageInstall the tool in the image, or run a command that is present
The container restarts although it exits with the code 0always or unless-stopped policy: it restarts even after a clean exitUse on-failure for a task that must finish
.RestartCount no longer goes up and the container is ExitedThe on-failure:n cap is reachedRead the code and the logs, fix the problem, then start the container again with docker start; recreate it only if its command or a setting applied at creation (variable, mount, port, image) is the cause
The loop resumes after a Docker restartalways policy, which also restarts a container stopped by hand, or on-failure, when the last exit was an error (for example a program that ignores the stop request and exits with the code 137)Switch to unless-stopped, or to no

How do you clean up after the tests?

The tests left six containers and a volume. docker rm -f deletes the containers and docker volume rm deletes the volume. The -f option first stops a container that is still running, including a container in Restarting. Delete the volume last, once the containers that use it are deleted.

Terminal window
docker rm -f sans-curl sans-droit via-shell tourne-en-rond limite-trois patient
docker volume rm attente

docker rm prints the deleted names and docker volume rm prints attente. After that, docker ps -a no longer lists any of these six names.

Practice

Two labs have you apply this method to containers that are already broken. You read the code, the status and the error, then you fix things.

In the first, three containers are stopped, each for a different reason. You note their exit codes and the status of one of them. Then you start a working container, with the same image and without rebuilding it.

In the second, three containers have the same fault. Docker restarts the first one endlessly, it gave up on the second, and the third works. You note the number of attempts, the exit code and the restart policy. Then you fix the problem so that the looping container stays up, without changing its policy or touching the working container.

In both labs, the grade does not depend on the commands you type. At the end, the lab queries Docker and checks the real state of the containers. Some values change at every run, so read them on your own containers: no answer key gives them.

Key points

  • The code 125 is the code of the docker run command when it fails itself, and Docker never gives it to a container: sometimes no container exists, sometimes it stays in the created state with its own code. An Exited (125) comes from the program and reads like any other code, in docker logs. The 126 means a file is not executable, the 127 that a command cannot be found.
  • A program that did not start wrote nothing. docker logs is empty, and the error is read in .State.Error with docker container inspect --format.
  • A code 127 can also come from a shell that is already running. In that case, its message is in the logs.
  • The Restarting status and a .RestartCount that goes up indicate a loop: Docker restarts a program that fails on every attempt.
  • on-failure:n is the only policy that gives up after n attempts. always and unless-stopped restart endlessly and differ only after a Docker restart.
  • Turning restarts off stops the loop without repairing anything. To repair, read the error and fix the problem outside the container: Docker then restarts the container on its own. Turning restarts off is only for examining the container while it is stopped.
  • You cannot change on an existing container its command, variables, mounts, ports or image. Recreating the container is the right answer when one of them is the cause, after reading why it was failing.

Knowledge check

Check that the essentials of this guide stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn