Docker Production Troubleshooting
The failures you will actually meet, each reproduced deliberately so you recognise the exact wording. Exit codes 125 through 137, restart loops, permission denials and the port conflict that reads like a networking bug.
Operations and Troubleshooting Guide 33 of 46 Advanced
- OSUbuntu 26.04 LTS (resolute)
- Docker Engine29.7.2
- Buildxv0.36.1
- Architectureamd64
- TimeAbout 16 min
- Reviewed22 August 2026
Tested on the versions above. Error wording changes between Docker versions. The exit codes and the diagnostic method are stable.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| DOCKER01 | 192.168.0.21 | Ubuntu 26.04 LTS | Docker Host | 2 Core | 4 GB | 50 GB |
Before you start
- Comfortable with inspect, logs and ps - guide 7 in this path.
- Resource limits and restart policies - guide 30 in this path.
-
Read the exit code before anything else
Docker's exit codes are a diagnosis, not noise. 125 means the DAEMON refused - the container never started. 126 means the command was found but could not be executed. 127 means it was not found at all. Anything else is your application's own exit status, passed through unchanged. Learning these four cases removes most of the guesswork.
bash docker run --rm alpine:3.22 sh -c "exit 42"; echo host-saw=$?host-saw=42# your exit status reaches the host untouched - 42 is not a Docker codeExpected resultThe application's own status, unmodified.
Success conditionYou can separate "Docker refused" from "the program failed". Everything below is one of those two.
-
127 and 126: the command is wrong
These two look alike and mean different things. 127 is a missing executable - a typo, or a binary that is not in a slim image. 126 is a file that exists but is not executable, which is usually a script missing its executable bit or its interpreter line. Note both errors mention the OCI runtime, which sends people looking in the wrong place.
bash docker run --rm alpine:3.22 notacommandunable to start container process: error during container init: exec: "notacommand": executable file not found in $PATH# exit 127docker run --rm alpine:3.22 /etc/hostnameunable to start container process: error during container init: exec: "/etc/hostname": permission denied# exit 126Expected resultTwo different messages, 127 then 126.
Success conditionYou can tell them apart from the wording alone. For 127 in a slim image, check the binary actually exists:
docker run --rm IMAGE which yourcommand. -
125: the daemon refused before your code ran
125 always means the container was never created. The two you will meet most are a missing image and a port already in use. The port message is long and mentions networking drivers and endpoints, which reads like a network fault - the operative words are at the very end.
bash docker run --rm nosuchimage:doesnotexistpull access denied for nosuchimage, repository does not exist or may require 'docker login'docker run -d --name cg-p2 -p 8099:80 nginx:alpinedriver failed programming external connectivity on endpoint cg-p2: Bind for 0.0.0.0:8099 failed: port is already allocatedExpected resultBoth refused with 125, neither container created.
Verify it worked
bash docker ps --filter publish=8099 --format "{{.Names}} {{.Ports}}"# names the container already holding the portSuccess conditionYou read to the end of the message. "Port is already allocated" is a conflict, not a driver problem - and note a private-registry image can produce the same "pull access denied" wording when you are simply not logged in.
-
137: killed, and how to tell why
137 is 128 plus signal 9. It appears for an out-of-memory kill, for
docker kill, and for a container that ignored SIGTERM during shutdown and got killed after the grace period. Only the OOMKilled flag distinguishes the first from the others.bash docker inspect cg-oom --format "oomkilled={{.State.OOMKilled}} exit={{.State.ExitCode}} status={{.State.Status}}"oomkilled=true exit=137 status=exitedExpected resultThe flag that turns an ambiguous code into a diagnosis.
Success conditionYou check the flag rather than assuming. If it is false, look at your stop grace period and whether the process handles SIGTERM.
-
The restart loop, and how to stop it
A container with an unbounded restart policy that fails immediately will spin forever.
docker psshows it as Restarting rather than Up, and the restart count climbs every few seconds. The trap is thatdocker logsshows only the current attempt, anddocker rm -fcan race the restart.docker update --restart=nobreaks the cycle first, then you can work calmly.bash Example session docker inspect cg-loop --format "restarts={{.RestartCount}} status={{.State.Status}}"restarts=7 status=restartingdocker ps --filter name=cg-loop --format "{{.Names}} {{.Status}}"cg-loop Restarting (1) Less than a second agoExpected resultA climbing restart count and the Restarting status, then a container that stays put.
Verify it worked
bash # stop the loop BEFORE investigating, so the container holds stilldocker update --restart=no cg-loopcg-loopSuccess conditionThe loop is stopped without destroying the evidence.
Restarting (1)also tells you the exit code it keeps failing with. -
Permission denied on a volume
The most common non-obvious failure in production. A new named volume is created root-owned, so a container running as a non-root user cannot write to it - and the image works fine in development where it runs as root. The error is a bare permission denied with no mention of ownership.
bash docker run --rm --user 1000:1000 -v cg-perm:/data alpine:3.22 sh -c "touch /data/f && echo wrote ok"touch: /data/f: Permission denieddocker run --rm -v cg-perm:/data alpine:3.22 ls -ld /datadrwxr-xr-x 2 root root 4096 Aug 20 08:20 /dataExpected resultThe write refused, and the directory owned by root.
Success conditionYou diagnosed it by listing ownership rather than guessing. Fix by chowning the mount point in an init step, or by running the service as the uid that owns the data.
-
exec on a container that is not running
docker execrequires a running container, and the error says so plainly - but it is easy to misread as the container being missing. A stopped container still exists; you simply cannot execute in it. To inspect a container that will not stay up, override its entrypoint and start a shell instead.bash docker stop cg-p1 >/dev/null && docker exec cg-p1 whoamiError response from daemon: container ea07324cba54... is not running# to debug an image that will not start, bypass its entrypoint:docker run --rm -it --entrypoint sh IMAGEExpected resultA clear "is not running", not "no such container".
Success conditionYou distinguish stopped from absent.
docker ps -ashows the former; only the latter is really gone. -
A method that works when nothing above matches
When the failure is unfamiliar, go in this order. It moves from cheapest to most invasive and each step narrows the next.
bash docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"# 1. state and exit code - is it Up, Exited, or Restartingdocker logs --tail 50 NAME# 2. what the application said before it stoppeddocker inspect NAME --format "{{.State.Status}} {{.State.ExitCode}} {{.State.OOMKilled}} {{.State.Error}}"# 3. the daemon's view, including errors that never reach the logsdocker events --since 10m --filter container=NAME# 4. the sequence of what happened, when the above is not enoughExpected resultFour commands you can run from memory.
Success conditionYou have an order to work in. Most failures are answered by the first two;
State.Errorcatches several that produce no logs at all.
Troubleshooting
The container exits immediately with status 0
Why: The main process finished. A container lives exactly as long as PID 1, so a command that returns, or a service started in the background by an entrypoint script, ends the container.
Fix:Run the service in the foreground. For nginx that means
daemon off;, for many images it is the default command you overrode.bash docker inspect NAME --format "{{.Config.Cmd}} {{.Config.Entrypoint}}"It works locally and fails on the server with no useful log
Why: Frequently an architecture mismatch or a missing environment variable. An arm64 image on an amd64 host fails at exec time with a format error.
Fix:Compare the image architecture with the host, and diff the environment.
bash docker image inspect IMAGE --format "{{.Os}}/{{.Architecture}}"docker exec NAME printenv | sortdocker: Cannot connect to the Docker daemon
Why: The daemon is not running, or your user is not in the docker group so the socket is unreadable.
Fix:Check the service first, then group membership. Adding a user to the docker group grants root-equivalent access - understand that before doing it.
bash systemctl is-active dockerid -nG | tr ' ' '\n' | grep -x dockerA container is unreachable although it is Up
Why: Either nothing is published, the service is bound to 127.0.0.1 inside the container, or it is simply not ready yet.
Fix:Check the published ports, then whether the process listens on 0.0.0.0, then readiness. Up is not ready - see guide 26.
bash docker port NAMEdocker exec NAME netstat -ltn 2>/dev/null || docker exec NAME ss -ltn