E-NO
Docker troubleshooting 9 Min Read

Docker Troubleshooting with Practical Examples: A Repeatable Guide

calendar_today Published: 2026-07-09
update Last Updated: 2026-08-06
analytics SEO Efficiency: 100%
Technical guide illustration for Docker Troubleshooting with Practical Examples: A Repeatable Guide.

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:

  1. 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
  1. Reproduce and capture context
  • Record the exact command, compose file, image tag, and error message.
  • Keep a minimal repro case if possible.
  1. Inspect container and image state
  • docker ps -a
  • docker inspect <container-or-image>
  1. Read logs first
  • docker logs <container> --since=10m -n 200
  • docker events --since 10m
  • Daemon logs (Linux): journalctl -u docker --since "10 min ago"
  1. Check networking
  • docker network ls
  • docker 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'
  1. Check volumes and permissions
  • docker inspect -f '{{.Mounts}}' <ctr>
  • ls -l on host paths; identify UID/GID mismatches
  1. Check build and cache
  • docker build . --no-cache (to validate a true rebuild)
  • docker history <image>
  1. Check resource pressure
  • docker stats (live view)
  • ulimit -a; check OOM kills in logs
  1. Fix, retest, and document briefly
  • Apply the smallest viable fix.
  • Rebuild/recreate only affected services.
  1. 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, e.g., 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:

  1. Inspect exit code and reason:
   docker inspect -f '{{.State.ExitCode}} {{.State.Error}}' <ctr>
  1. Show last logs:
   docker logs --tail=100 <ctr>
  1. Run the image interactively to see the process lifecycle:
   docker run --rm -it <image> sh
  1. Confirm CMD/ENTRYPOINT:
   docker inspect -f '{{.Config.Entrypoint}} {{.Config.Cmd}}' <image>

Common causes and fixes:

  • Process ended normally (exit 0). Add a foreground process or use a proper server command. For quick debugging:
  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
  • Exit code 125 often means Docker cannot pull the image. Run docker pull <image> manually to see the underlying error.

Networking and Ports

Symptom: service does not respond on the expected port, or bind fails with "address already in use\).

Checklist:

  1. Verify port mappings:
   docker ps --format 'table {{.Names}}\t{{.Ports}}'
  1. Check host port conflicts:
   # Linux/macOS
   sudo lsof -i :8080 || sudo ss -lntp | grep ':8080'
  1. Confirm the service is listening inside the container:
   docker exec -it <ctr> sh -c 'ss -lntp || netstat -lntp'
  1. 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:8080 when 8080 is in use.
  • Bind to 0.0.0.0 inside the container if the app binds to 127.0.0.1 by default (e.g., CMD ["python", "-m", "http.server", "8080", "--bind", "0.0.0.0\]).
  • 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:

  1. Check resolver inside the container:
   cat /etc/resolv.conf
  1. Test DNS:
   docker run --rm busybox:1.36 getent hosts example.com || nslookup example.com
  1. Corporate proxy case:
  • Pass HTTP_PROXY, HTTPS_PROXY, and NO_PROXY to 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_PROXY for internal hostnames and service names on user-defined networks.

Volumes and Permissions

Symptom: Permission denied when writing to a bind mount or volume.

Checklist:

  1. Inspect mounts:
   docker inspect -f '{{json .Mounts}}' <ctr> | jq .
  1. Check host path ownership:
   ls -l /host/path
   stat -c '%u:%g %n' /host/path   # Linux
  1. Find the container process UID/GID:
   docker exec -it <ctr> id

Fixes:

  • Align UIDs: chown -R 1000:1000 /host/path to 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 :Z or :z to 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.

  • Stale cache or bad layer ordering:
  • Use .dockerignore to reduce context (e.g., exclude node_modules, .git, *.log).
  • Move apt-get install and package.json steps 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)_PROXY and NO_PROXY in Dockerfile with ARG / 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:

  • Verify imagePullSecrets and that the registry is reachable from the cluster nodes.

Health probes:

  • Example readiness probe for an HTTP service on port 8080:
  readinessProbe:
    httpGet:
      path: /healthz
      port: 8080
    initialDelaySeconds: 5
    periodSeconds: 10

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)_PROXY and NO_PROXY and verify with docker 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 three failure scenarios, and document fixes.

Scope (keep it small):

  • One compose file with nginx:alpine serving a static index.html.
  • Healthcheck: GET http://localhost:8080/ returns 200.

Steps:

  1. Baseline run:
   docker compose up -d
   curl -v http://localhost:8080/
  1. Add a deliberate port conflict:
  • Start a process on 8080 (e.g., python -m http.server 8080), then observe compose error and switch to 8081.
  1. Add a bad mount permission:
  • Bind mount a read-only directory and confirm 403 or write errors; fix ownership or use a volume.
  1. Add a DNS failure in a sidecar curl job:
  • Run a curl container on the same network to test external DNS; add --dns override if needed.
  1. Capture and store commands and outputs in a troubleshooting.md file 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.

Related Research

Article Quality Score

Reader usefulness 100%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL