This guide shows how to troubleshoot Docker with concrete commands, short checklists, and small recovery workflows. It targets developers, DevOps consultants, and startup teams who need fast, safe fixes. You will learn a repeatable flow, see common failure patterns, and apply practical examples that work on Linux and macOS hosts, and inside Docker Compose and Kubernetes. The goal: make issues easy to reproduce, measure, and resolve locally before wider rollout.
Workflow Overview
Use this repeatable flow for most Docker issues:
- Verify the basics
- Check Docker is running:
- Linux:
systemctl status docker - All:
docker info - Confirm disk and memory are not exhausted:
df -h,free -m(Linux),docker system df
- Reproduce and capture context
- Record the exact command, compose file, image tag, and error message.
- Keep a small repro case if possible.
- Inspect container and image state
docker ps -adocker inspect <container-or-image>
- Read logs first
docker logs <container> --since=10m -n 200docker events --since 10m- Daemon logs (Linux):
journalctl -u docker --since "10 min ago"
- Check networking
docker network lsdocker network inspect <net>- Test from host:
curl -v http://localhost: PORT - Test from container:
docker exec -it <ctr> sh -c 'curl -v http://service: port'
- Check volumes and permissions
docker inspect -f '{{.Mounts}}' <ctr>ls -lon host paths; identify UID/GID mismatches
- Check build and cache
docker build . --no-cache(to validate a true rebuild)docker history <image>
- Check resource pressure
docker stats(live view)ulimit -a; check OOM kills in logs
- Fix, retest, and document briefly
- Apply the smallest viable fix.
- Rebuild/recreate only affected services.
- Cleanup safely (optional)
- List before prune:
docker system df - Targeted prune:
docker image prune -f,docker builder prune -f - Avoid destructive global prunes on shared hosts.
Logs and Diagnostics
Start with logs and a quick snapshot of state.
Commands you will use frequently:
- Container logs:
docker logs <ctr>
docker logs -f --since=15m <ctr>
- Daemon logs (Linux):
journalctl -u docker -n 200 --no-pager
- Live events timeline:
docker events --since 30m
- State and health:
docker inspect -f '{{.State.Status}} {{.State.Health.Status}}' <ctr>
- Networking:
docker network ls
docker network inspect <net>
- Compose:
docker compose ps
docker compose logs -f --since=10m
Tip: capture command outputs into text files for later diffing, for example docker inspect > inspect.json.
Containers Exit Immediately
Symptom: docker run exits with code 0 or a nonzero code, and the service never stays up.
Checklist:
- Inspect exit code and reason:
docker inspect -f '{{.State.ExitCode}} {{.State.Error}}' <ctr>
- Show last logs:
docker logs --tail=100 <ctr>
- Run the image interactively to see the process lifecycle:
docker run --rm -it <image> sh
- Confirm CMD/ENTRYPOINT:
docker inspect -f '{{.Config.Entrypoint}} {{.Config.Cmd}}' <image>
Common causes and fixes:
- The process ended normally (exit 0). Add a foreground process or use a proper server command. For quick debugging use:
docker run -it --entrypoint sh <image>
# or keep it alive temporarily
docker run -d --name debug <image> sh -c 'tail -f /dev/null'
- Wrong ENTRYPOINT. Prefer an entrypoint script that execs the final process so signals work:
#!/bin/sh
set -e
exec "$@"
- Missing dependencies at runtime. Validate inside the container:
ldd /path/to/binary || true
which node || which python || which nginx
Networking and Ports
Symptom: service does not respond on the expected port, or bind fails with "address already in use".
Checklist:
- Verify port mappings:
docker ps --format 'table {{.Names}}\t{{.Ports}}'
- Check host port conflicts:
# Linux/macOS
sudo lsof -i :8080 || sudo ss -lntp | grep ':8080'
- Confirm the service is listening inside the container:
docker exec -it <ctr> sh -c 'ss -lntp || netstat -lntp'
- Test reachability:
# From host
curl -v http://localhost:8080
# From another container on same network
docker run --rm --network <net> curlimages/curl:8.7.1 curl -sv http://<service>:<port>
Fixes:
- Change the host port mapping:
-p 8081:8080when 8080 is in use. - Bind to
0.0.0.0inside the container if the app binds to127.0.0.1by default. - Validate container network:
docker network inspect <net>and ensure both services share the same network.
DNS and Proxy Issues
Symptom: containers cannot resolve names or cannot reach the internet behind a proxy.
Checklist:
- Check resolver inside the container:
cat /etc/resolv.conf
- Test DNS:
docker run --rm busybox:1.36 getent hosts example.com || nslookup example.com
- Corporate proxy case:
- Pass
HTTP_PROXY,HTTPS_PROXY, andNO_PROXYto builds and runs. - For builds:
docker build --build-arg HTTP_PROXY=$HTTP_PROXY --build-arg HTTPS_PROXY=$HTTPS_PROXY .
- Override DNS if needed:
docker run --dns 8.8.8.8 --dns 1.1.1.1 <image>
Fixes:
- Ensure the daemon-level DNS is configured only if necessary; prefer per-container flags.
- Set
NO_PROXYfor internal hostnames and service names on user-defined networks.
Volumes and Permissions
Symptom: Permission denied when writing to a bind mount or volume.
Checklist:
- Inspect mounts:
docker inspect -f '{{json .Mounts}}' <ctr> | jq .
- Check host path ownership:
ls -l /host/path
stat -c '%u:%g %n' /host/path # Linux
- Find the container process UID/GID:
docker exec -it <ctr> id
Fixes:
- Align UIDs:
chown -R 1000:1000 /host/pathto match the app user, or run with-u 1000:1000. - Use a named volume to avoid host permission quirks for app data:
docker volume create appdata && docker run -v appdata:/var/lib/app ...
- SELinux hosts: add
:Zor:zto bind mounts to relabel context when appropriate. - Validate Nginx and static content permissions in web stacks:
docker exec -it <ctr> sh -c 'nginx -t && ls -l /usr/share/nginx/html'
Build and Image Issues
Common build failures and slow builds.
Symptoms and fixes:
- Stale cache or bad layer ordering:
- Use
.dockerignoreto reduce context. - Move
apt-get installandpackage.jsonsteps earlier for better caching. - Validate a clean rebuild:
docker build --no-cache -t app: test . - Network failures during RUN:
- Retry with a base image that includes curl to debug network.
- Respect
HTTP(S)_PROXYandNO_PROXYin Dockerfile withARG/ENV. - No space left on device:
- Inspect:
docker system df,docker builder du - Prune safely:
docker image prune -f
docker builder prune -f
- Remove only unneeded dangling images; avoid global prune on shared hosts.
- Large images:
- Use multi-stage builds to copy only final artifacts.
- Prefer alpine or distro-specific minimal images when compatible.
Docker Compose Tips
Debugging multi-service apps.
Useful commands:
docker compose ps
docker compose logs -f service
docker compose config
Service readiness:
- Add healthchecks and make dependents wait for readiness instead of startup order.
Example healthcheck and simple wait loop:
services:
db:
image: postgres:16
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 5s
timeout: 3s
retries: 10
api:
build: ./api
depends_on:
- db
environment:
DATABASE_URL: postgres://postgres@db:5432/app
command: sh -lc 'until pg_isready -h db -U postgres; do sleep 1; done; exec ./api'
Networking:
- Ensure services share the same user-defined network (default for Compose).
Kubernetes Edge Cases
When containers run in Kubernetes, map cluster symptoms back to container behavior.
Checklist:
- Find pod and events:
kubectl get pods -n <ns>
kubectl describe pod/<name> -n <ns>
- Logs:
kubectl logs <pod> -n <ns> --previous
Check image tag, registry auth, and pull policy.
Ensure readiness and liveness probes match the container's real startup time and port.
- Image pull issues:
- Health probes:
Local reproduction:
- Pull the same image locally and run with similar env and command to debug faster:
docker run --rm -e VAR=val <image>:<tag>
- If CrashLoopBackOff, focus on container logs and entrypoint arguments first.
GitLab CI/CD Notes
Common Docker runner patterns.
Docker-in-Docker (dind) for building images:
image: docker:27
services:
- name: docker:27-dind
command: ['--mtu=1460']
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ''
stages: [build]
build:
stage: build
script:
- docker info
- docker build -t registry.example.com/app:$CI_COMMIT_SHORT_SHA .
- docker push registry.example.com/app:$CI_COMMIT_SHORT_SHA
Tips:
- Set
DOCKER_TLS_CERTDIR=''when you intentionally use plain TCP with dind in trusted runners. - If name resolution fails in jobs, pass
HTTP(S)_PROXYandNO_PROXYand verify withdocker run busybox getent hosts. - For bind mounts in the runner, align UID/GID or use a volume to avoid permission errors.
Local Pilot Plan
Start with a small, measurable pilot you can inspect locally.
Pilot goal: stand up a simple Nginx service via Docker Compose, add healthchecks, rehearse 3 failure scenarios, and document fixes.
Scope (keep it small):
- One compose file with
nginx: alpineserving a staticindex.html. - Healthcheck: GET
http://localhost:8080/returns 200.
Steps:
- Baseline run:
docker compose up -d
curl -v http://localhost:8080/
- Add a deliberate port conflict:
- Start a process on 8080, then observe compose error and switch to 8081.
- Add a bad mount permission:
- Bind mount a read-only dir and confirm 403 or write errors; fix ownership or use a volume.
- Add a DNS failure in a sidecar curl job:
- Run a curl container on the same network to test external DNS; add
--dnsoverride if needed.
- Capture and store commands and outputs in a
troubleshooting.mdfile under the repo.
Success criteria:
- All three failures reproduced and resolved locally.
- Healthcheck passes consistently.
- Clear list of commands and expected outputs for each scenario.
Conclusion
You now have a practical flow to triage Docker issues, read the right logs, and apply safe, minimal fixes. Start with service state and logs, confirm networking, and check volumes, permissions, and build layers. For Compose, rely on healthchecks and simple waits rather than startup order. In Kubernetes and CI, map symptoms back to the same container fundamentals. Begin with a small local pilot, measure outcomes, and extend the approach service by service.