Docker and Kubernetes course menu
How to Debug Docker Containers
Find out why a container exits or misbehaves: exit codes, logs, inspect, running a shell, overriding the entrypoint, debugging builds, and checking networks.
On this page
Containers fail for a few common reasons, and a fixed routine finds most of them.
Step 1: What state is it in?#
docker run -d --name broken alpine:3.24 sh -c 'echo "Starting"; echo "Config missing" >&2; exit 3' > /dev/null
sleep 1
docker ps --format '{{.Names}}' | wc -l
docker ps -a --format '{{.Names}} {{.Status}}' | sed 's/ [0-9a-zA-Z ]* ago//'0
broken Exited (3)It is not in docker ps, because it has exited. ps -a shows it, with the exit code.
Step 2: Read the logs#
Logs work on stopped containers too:
docker logs broken
docker rm broken > /dev/nullStarting
Config missingThis is usually enough to find the cause.
Step 3: Inspect it#
docker inspect shows everything Docker knows. --format picks out the useful parts:
docker run -d --name web -p 8080:80 -e MODE=test nginx:1.30-alpine > /dev/null
docker inspect --format '{{.State.Status}} since {{.State.Running}}' web
docker inspect --format '{{json .NetworkSettings.Ports}}' web
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' web | grep MODErunning since true
{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"},{"HostIp":"::","HostPort":"8080"}]}
MODE=testStep 4: Look inside#
For a running container, open a shell with docker exec -it web sh. One-off checks work without one:
docker exec web ls /etc/nginx/conf.d
docker exec web ps | head -n 3
docker rm -f web > /dev/nulldefault.conf
PID USER TIME COMMAND
1 root 0:00 nginx: master process nginx -g daemon off;
30 nginx 0:00 nginx: worker processDebugging a container that exits at once#
You cannot exec into a stopped container. Instead, start the image with a different command, and look around:
mkdir app
cd app
cat > Dockerfile <<'EOF'
FROM alpine:3.24
WORKDIR /app
COPY start.sh .
CMD ["./start.sh"]
EOF
echo 'echo hi' > start.sh
docker build -q -t bad-app . > /dev/null
docker run --rm bad-app
echo "Exit 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: "./start.sh": permission denied
Run 'docker run --help' for more information
Exit code: 126Permission denied. Override the command to look inside:
docker run --rm bad-app ls -l /apptotal 4
-rw-rw-r-- 1 root root 8 Nov 5 07:04 start.shThe script is not executable. Fix it, and add a shebang:
printf '#!/bin/sh\necho hi\n' > start.sh
chmod +x start.sh
docker build -q -t bad-app . > /dev/null
docker run --rm bad-apphiIf an image has an ENTRYPOINT, replace it with docker run --entrypoint sh -it image.
Debugging a failed build#
When a step fails, the error shows that step's output:
cd ~
mkdir bad
cd bad
cat > Dockerfile <<'EOF'
FROM alpine:3.24
RUN apk add --no-cache this-package-does-not-exist
EOF
docker build --progress=plain -t bad . 2>&1 | grep -E 'ERROR: unable|no such package|did not complete' | head -n 3 | cut -c1-80#5 ERROR: process "/bin/sh -c apk add --no-cache this-package-does-not-exist" diRun the same step by hand to explore: docker run -it --rm alpine:3.24 sh.
Debugging networks#
Most network problems come down to a name, a port, or a network:
docker network create dbg > /dev/null
docker run -d --name api --network dbg nginx:1.30-alpine > /dev/null
sleep 2
docker run --rm --network dbg alpine:3.24 sh -c 'nslookup api > /dev/null 2>&1 && echo "Name works"; nc -z -w 2 api 80 && echo "Port open"; nc -z -w 2 api 81 || echo "Port 81 closed"'
docker rm -f api > /dev/null
docker network rm dbg > /dev/nullPort open
Port 81 closedImages are often very small and lack tools. Attach a tool container to another container's network with --network container:name. The image nicolaka/netshoot is popular for this, and it is full of network tools.
Watch events#
docker events shows what the daemon is doing live, which helps with restart loops:
docker events --since 30s --until 0s --filter event=die --format '{{.Actor.Attributes.name}} died' | tail -n 2cranky_snyder died
wizardly_shirley diedA troubleshooting routine#
docker ps -a: is it running? What is the exit code?docker logs name: what did it say?docker inspect name: are the settings right?docker exec -it name sh: what do things look like inside?docker run -it --rm image sh: try it by hand.docker stats: is it out of memory or CPU?
Common mistakes#
Practice#
Practice exercise
Run an Alpine container that exits with code 7, then print its exit code using docker inspect.
Show solution
docker run --name exit7 alpine:3.24 sh -c 'exit 7'
docker inspect --format '{{.State.ExitCode}}' exit7
docker rm exit7 > /dev/null7Key takeaway#
Next: Container Security.
Finished this lesson?
Related lessons
Recommended Docker and Kubernetes resources
- Docker Deep Dive by Nigel Poulton
A practical guide to how Docker works, for readers who have finished the basics.
Some links are affiliate links. If you buy through them, Laviyx Learn may earn a commission at no extra cost to you. As an Amazon Associate, we earn from qualifying purchases.