Docker Troubleshooting Guide: Debug and Fix Common Issues

Published February 12, 2026 · 25 min read

Something broke. Your container will not start, your build fails on line 47, or two services cannot talk to each other. You need answers, not theory. This guide covers the most common Docker problems developers actually hit, with real error messages and concrete solutions you can copy and run.

Table of Contents

  1. First Steps: The Universal Debugging Workflow
  2. Container Won't Start or Exits Immediately
  3. Build Failures
  4. Network Problems
  5. Volume and Permission Issues
  6. Performance Problems
  7. Docker Compose Issues
  8. Registry and Image Pull Errors
  9. Advanced Debugging Techniques
  10. Common Error Messages Reference
  11. Docker Desktop vs Linux Daemon Differences

1. First Steps: The Universal Debugging Workflow

Before diving into specific problems, here is the sequence that solves 80% of Docker issues:

# Step 1: Check if the container is running
docker ps -a
# Look at STATUS column — "Exited (1)" means crash, "Exited (0)" means normal exit

# Step 2: Read the logs
docker logs my-container
# Add --tail 50 to see last 50 lines, or -f to follow live

# Step 3: Inspect the container configuration
docker inspect my-container
# Check: Cmd, Entrypoint, Env, Mounts, NetworkSettings

# Step 4: Check Docker daemon status
sudo systemctl status docker

# Step 5: Check disk space (Docker fails silently when disk is full)
docker system df
df -h /var/lib/docker

If your disk is full, run docker system prune -a to reclaim space from stopped containers, unused images, and dangling build cache. This single command fixes a surprising number of mysterious failures.

2. Container Won't Start or Exits Immediately

Reading Exit Codes

Every stopped container has an exit code. This is your first clue:

# Get the exit code
docker inspect my-container --format='{{.State.ExitCode}}'

# Exit code meanings:
# 0   — Process completed successfully (not a crash)
# 1   — Application error (check logs)
# 126 — Command cannot execute (permission problem)
# 127 — Command not found (wrong CMD/ENTRYPOINT)
# 137 — Killed by SIGKILL (OOM killer or docker kill)
# 139 — Segmentation fault (SIGSEGV)
# 143 — Killed by SIGTERM (docker stop)

Exit Code 1: Application Error

# The application crashed. Read the logs:
docker logs my-container 2>&1

# Common causes: missing env var, can't connect to DB, config file not found
# Example: Node.js app can't reach database
docker logs my-app
# Error: connect ECONNREFUSED 127.0.0.1:5432
# Fix: the app looks for DB at localhost, but DB is in another container.
# Use the service name instead:
docker run -e DATABASE_URL=postgres://db:5432/mydb my-app

Exit Code 127: Command Not Found

# Your CMD or ENTRYPOINT references a binary that doesn't exist
docker logs my-container
# /bin/sh: my-app: not found

# Common causes: typo in CMD, binary not in PATH,
# Alpine image with glibc-linked binary (Alpine uses musl)

# Debug: check what's actually in the image
docker run --rm -it my-image /bin/sh
ls -la /app/
which my-app
ldd /app/my-app  # Check for missing shared libraries

Exit Code 137: Out of Memory

# Container killed because it exceeded its memory limit
docker inspect my-container --format='{{.State.OOMKilled}}'
# true = confirmed OOM kill

# Fix: increase the memory limit
docker run -m 512m my-image        # Set to 512MB
docker run -m 1g my-image          # Set to 1GB
docker run -m 2g --memory-swap 2g my-image  # 2GB hard limit, no swap

Container Starts Then Immediately Stops

# Most common reason: CMD runs a non-daemon process
# CMD ["echo", "hello"]  — exits immediately

# Web servers that daemonize by default:
# Bad:  CMD ["nginx"]                           (daemonizes and exits)
# Good: CMD ["nginx", "-g", "daemon off;"]      (stays in foreground)

# Shell scripts must exec the final command:
#!/bin/bash
# setup code here...
exec java -jar /app/server.jar   # exec replaces shell with java
# Without exec, the shell is PID 1 and won't forward signals

3. Build Failures

COPY Failed: File Not Found

# Error: COPY failed: file not found in build context

# COPY paths are relative to the build context (the directory you
# pass to docker build), NOT relative to the Dockerfile location
docker build -f Dockerfile .    # "." is the build context

# Common mistakes:
# COPY ../config /app/config    # Cannot go above build context!
# COPY /etc/config /app/config  # Absolute host paths don't work

# Check .dockerignore — it might exclude the file you need
cat .dockerignore
# If it contains "*.json", it will exclude package.json too!

BuildKit Output Is Hidden

# BuildKit collapses output by default, hiding the actual error
# Fix: use plain progress output
DOCKER_BUILDKIT=1 docker build --progress=plain -t my-image .

# To see full output including cached layers:
docker build --progress=plain --no-cache -t my-image .

Layer Cache Problems

# Problem: build uses a cached layer with old dependencies

# Fix 1: bust the cache entirely
docker build --no-cache -t my-image .

# Fix 2: structure your Dockerfile to cache dependencies properly
# Bad order (cache busted on every code change):
COPY . /app
RUN npm install

# Good order (cache preserved when only code changes):
COPY package.json package-lock.json /app/
RUN npm install
COPY . /app

Multi-Stage Build: File Missing in Final Stage

# Build succeeds but file not found at runtime — you forgot to
# COPY the artifact from the build stage to the final stage

FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm run build

FROM node:20-slim
WORKDIR /app
# You MUST copy everything the final stage needs:
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

apt-get Fails During Build

# Error: E: Unable to locate package some-package
# Always run apt-get update and install in the SAME RUN command:
RUN apt-get update && apt-get install -y \
    curl ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# WRONG — separate layers mean update cache goes stale:
RUN apt-get update
RUN apt-get install -y curl   # May fail with stale index

4. Network Problems

Container Cannot Resolve DNS

# Error: Could not resolve host: api.example.com

# Check DNS inside the container
docker exec my-container cat /etc/resolv.conf
docker exec my-container nslookup google.com
docker exec my-container ping -c 2 8.8.8.8

# If ping works but DNS doesn't — DNS server misconfiguration
# Fix: specify DNS servers
docker run --dns 8.8.8.8 --dns 8.8.4.4 my-image

# Or globally in /etc/docker/daemon.json:
{ "dns": ["8.8.8.8", "8.8.4.4"] }

Container-to-Container Communication Fails

# The default bridge does NOT have DNS resolution — containers can
# only communicate via IP, not by name

# Fix: create a user-defined network
docker network create app-net
docker run -d --name api --network app-net my-api
docker run -d --name web --network app-net my-web
# Now "web" can reach "api" by name: curl http://api:8080

# In Docker Compose, services are on the same network by default.
# Use the service name (e.g., "db"), not the container name

Port Binding Errors

# Error: Bind for 0.0.0.0:80 failed: port is already allocated

# Find what's using the port
sudo lsof -i :80
sudo ss -tlnp | grep :80
docker ps -a --filter "publish=80"   # Check for Docker containers

# Fix: use a different host port
docker run -p 8080:80 my-image   # Map host 8080 to container 80

Cannot Connect to Host Service from Container

# Container needs to reach a service running on the host machine

# Docker Desktop (Mac/Windows) — works out of the box:
docker run -e API_URL=http://host.docker.internal:3000 my-image

# Linux — must add the host mapping explicitly:
docker run --add-host=host.docker.internal:host-gateway \
    -e API_URL=http://host.docker.internal:3000 my-image

# Alternative: use the bridge gateway IP (usually 172.17.0.1)
docker network inspect bridge --format='{{(index .IPAM.Config 0).Gateway}}'

5. Volume and Permission Issues

Permission Denied on Bind Mount

# Error: EACCES: permission denied, open '/data/config.json'

# Check what user the container runs as:
docker exec my-container id
# uid=1000(node) gid=1000(node) groups=1000(node)

# Fix 1: change host ownership to match container's user
sudo chown -R 1000:1000 /path/to/host/dir

# Fix 2: run container as your host user
docker run --user "$(id -u):$(id -g)" -v /path:/data my-image

# Fix 3: set ownership in Dockerfile
RUN mkdir -p /data && chown 1000:1000 /data
USER 1000

Named Volume Has Stale Data

# Named volumes persist data — they are NOT overwritten on container recreate

# Check volume contents:
docker run --rm -v my-volume:/data alpine ls -la /data

# Fix: remove the volume and recreate
docker volume rm my-volume
docker compose down -v   # Removes all project volumes

SELinux Blocking Volume Access

# On RHEL/CentOS/Fedora, SELinux blocks container access to bind mounts
# Permissions look correct but you still get "permission denied"

# Fix: add :z or :Z suffix to the volume mount
docker run -v /host/path:/container/path:z my-image   # Shared label
docker run -v /host/path:/container/path:Z my-image   # Private label

# Check if SELinux is the culprit:
getenforce                         # "Enforcing" = active
sudo ausearch -m avc -ts recent    # Check for denials

6. Performance Problems

Container Using Too Much Memory

# Monitor real-time resource usage
docker stats   # Shows CPU%, MEM USAGE/LIMIT, NET I/O, BLOCK I/O

# Set memory limits
docker run -m 256m my-image                    # 256MB limit
docker run -m 1g --memory-swap 1g my-image     # 1GB, no swap

# In docker-compose.yml:
services:
  api:
    image: my-api
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '1.0'

Slow Build Times

# Fix 1: use BuildKit (much faster than legacy builder)
DOCKER_BUILDKIT=1 docker build -t my-image .

# Fix 2: optimize layer ordering (dependencies before source code)
COPY package.json package-lock.json ./
RUN npm ci --production
COPY src/ ./src/

# Fix 3: use .dockerignore to shrink the build context
# node_modules, .git, *.md, test/, coverage/, .env*

# Fix 4: use BuildKit cache mounts for package managers
RUN --mount=type=cache,target=/root/.npm npm ci

Bind Mount Performance on Mac/Windows

# Bind mounts are slow on Docker Desktop (file sync between host and VM)

# Fix: use named volumes for generated files like node_modules
services:
  app:
    volumes:
      - .:/app                                 # Code (hot reload)
      - node_modules:/app/node_modules         # Fast named volume
volumes:
  node_modules:

7. Docker Compose Issues

depends_on Does Not Wait for Service Ready

# depends_on only waits for the container to START, not the app inside

# Fix: use healthchecks with depends_on condition
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5
  web:
    image: my-web-app
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://postgres:secret@db:5432/mydb

Environment Variables Not Substituted

# ${MY_VAR} in docker-compose.yml shows up as empty string

# Common mistakes:
# 1. .env file in wrong directory (must be next to compose file)
# 2. Quotes in .env: MY_VAR="value" includes the quotes in the value
# 3. Variable set in container env, not compose-file interpolation

# Debug: see what Compose actually resolves
docker compose config   # Shows the fully-resolved compose file

Compose Network and Container Conflicts

# Error: network myproject_default: network already exists
docker compose down      # Removes containers and networks for this project
docker network prune     # Removes all unused networks

# Compose recreates containers every time? Check what changed:
docker compose config --hash="*"   # Show config hashes per service
docker compose up -d               # Only recreates if config changed
docker compose up -d --no-recreate # Never recreate running containers

8. Registry and Image Pull Errors

Docker Hub Rate Limiting

# Error: toomanyrequests: You have reached your pull rate limit
# Anonymous: 100 pulls/6h per IP. Authenticated: 200 pulls/6h.

# Fix 1: log in to Docker Hub (doubles your limit)
docker login

# Fix 2: use a registry mirror in /etc/docker/daemon.json:
{ "registry-mirrors": ["https://mirror.gcr.io"] }

# Fix 3: cache base images locally — subsequent builds won't count
docker pull alpine:3.19
docker pull node:20-slim

Image Not Found / Manifest Unknown

# Error: manifest for myimage:latest not found

# Check 1: typo in image name? (ngingx vs nginx)
# Check 2: does the tag exist on Docker Hub?
# Check 3: wrong platform? (ARM Mac pulling amd64-only image)
docker pull --platform linux/amd64 my-image:latest
# Check 4: private registry needs auth
docker login my-registry.example.com

9. Advanced Debugging Techniques

Shell Into a Running Container

docker exec -it my-container /bin/bash
# Alpine/distroless: docker exec -it my-container /bin/sh
# As root: docker exec -it --user root my-container /bin/bash

# Run specific commands without a shell:
docker exec my-container env              # Environment variables
docker exec my-container cat /etc/hosts   # Hosts file
docker exec my-container netstat -tlnp    # Listening ports

Debug a Container That Won't Start

# Override the entrypoint to get a shell:
docker run -it --entrypoint /bin/sh my-image

# Or keep the container alive with sleep:
docker run -d --name debug my-image sleep infinity
docker exec -it debug /bin/sh

# Extract files from an image without running it:
docker create --name temp my-image
docker cp temp:/app/config.json ./config.json
docker rm temp

Inspect Filesystem Changes and Monitor Events

# See what files the container modified vs its image:
docker diff my-container
# A /app/data/cache.db   (Added)
# C /app/config           (Changed)
# D /tmp/old-file         (Deleted)

# Copy files in and out:
docker cp my-container:/app/logs/error.log ./error.log
docker cp ./fixed-config.json my-container:/app/config.json

# Watch Docker events in real time:
docker events --filter event=die     # Container crashes
docker events --filter event=oom     # Out of memory events

10. Common Error Messages Reference

Daemon Connection Errors

# Error: Cannot connect to the Docker daemon at unix:///var/run/docker.sock

# Fix 1: start the Docker service
sudo systemctl start docker

# Fix 2: your user is not in the docker group
sudo usermod -aG docker $USER
newgrp docker   # Apply without logout

# Fix 3: Docker Desktop is not running (Mac/Windows)

Disk Space Errors

# Error: no space left on device

# Check Docker disk usage:
docker system df

# Nuclear option: remove everything unused
docker system prune -a --volumes

# Safer: remove individually
docker container prune    # Stopped containers
docker image prune -a     # Unused images
docker volume prune       # Unused volumes
docker builder prune      # Build cache

Other Common Errors

# "OCI runtime create failed: unable to start container process"
# Binary in CMD/ENTRYPOINT doesn't exist. Check: docker inspect image --format='{{.Config.Cmd}}'

# "exec format error"
# Image built for different CPU architecture. Fix: docker run --platform linux/amd64 my-image

# "COPY failed: no source files were specified"
# Glob in COPY matched zero files. Check .dockerignore isn't excluding them.

# "max depth exceeded"
# Too many image layers (127 limit). Combine RUN commands with &&

# "dial tcp: lookup registry-1.docker.io: no such host"
# DNS failure. Check internet connection. Try: docker run --dns 8.8.8.8 alpine ping google.com

11. Docker Desktop vs Linux Daemon Differences

Many troubleshooting steps differ between Docker Desktop (Mac/Windows) and Docker Engine on Linux:

# host.docker.internal
# Mac/Windows: works out of the box
# Linux: requires --add-host=host.docker.internal:host-gateway

# File system performance
# Mac/Windows: bind mounts are slow (file sync between host and VM)
# Linux: bind mounts are native speed (direct filesystem access)

# Networking
# Mac/Windows: --network host does NOT work (containers run in a VM)
# Linux: host networking works, containers share host kernel

# File permissions
# Mac/Windows: Docker Desktop maps file ownership automatically
# Linux: UID/GID must match between container and host

# Resource limits
# Mac/Windows: set in Docker Desktop preferences (VM allocation)
# Linux: controlled by cgroups, limited by actual host resources
⚙ Essential tools: Build Dockerfiles with the Dockerfile Builder, validate Compose files with the Docker Compose Validator, and keep the Docker Commands Cheat Sheet handy.

Frequently Asked Questions

Why does my Docker container exit immediately after starting?

A container exits when its main process (PID 1) terminates. Common causes: the application crashes on startup (check docker logs), CMD runs a short-lived command instead of a server, a required env var or config file is missing, or the entrypoint script does not exec the final command. Check the exit code with docker inspect container --format='{{.State.ExitCode}}' — 0 means normal exit, 1 means app error, 137 means OOM killed, 139 means segfault.

How do I fix "permission denied" errors with Docker volumes?

The container's user does not match the file owner on the host. Check with docker exec my-container id. Fix: change host ownership with sudo chown -R UID:GID /path, run as your host user with --user "$(id -u):$(id -g)", use named volumes (Docker manages permissions), or add RUN chown in your Dockerfile. On RHEL/Fedora, also check SELinux — add :z to the mount.

Why can't my Docker container connect to another container by name?

DNS resolution only works on user-defined networks, not the default bridge. Create one with docker network create my-net and connect both containers. In Compose, use the service name (e.g., db), not the container name. Verify both containers are running and the target is listening on the expected port.

How do I debug a Docker build that keeps failing?

Use --progress=plain to see full output. Use --no-cache to rule out stale layers. Add temporary RUN ls -la before the failing step. Check .dockerignore is not excluding needed files. COPY paths are relative to the build context, not the Dockerfile.

What does "port is already allocated" mean?

Another process is listening on that host port. Find it with sudo lsof -i :PORT or sudo ss -tlnp | grep PORT. Fix: stop the conflicting service, map to a different host port (-p 8081:80), or remove stale containers. Restarting the Docker daemon clears stuck port bindings.

Why is my Docker image so large?

Use docker history my-image to find the largest layers. Fixes: multi-stage builds, slim/Alpine base images, combine RUN commands and clean caches in the same layer, and add a .dockerignore to exclude node_modules, .git, and test directories.

How do I fix Docker Compose service startup order?

depends_on only waits for the container to start, not the app. Use condition: service_healthy with a healthcheck: add healthcheck: test: ["CMD-SHELL", "pg_isready"] to the dependency, then depends_on: db: condition: service_healthy on the dependent service.

Conclusion

Most Docker problems fall into a handful of categories: the container's main process exits (check logs and exit codes), the build cache is stale or files are missing (use --no-cache and check .dockerignore), containers cannot communicate (use user-defined networks), or permissions are wrong on volumes (match UID/GID between host and container).

Start every debugging session with docker logs and docker inspect. When you need to go deeper, override the entrypoint with a shell, use docker diff to see filesystem changes, or run a nicolaka/netshoot container on the same network for diagnostics. Keep docker system df in mind — many cryptic errors are simply disk space running out.

Learn More

Related Resources

Docker Complete Guide
Master Docker from basics to advanced production patterns
Docker Compose Guide
Multi-container orchestration with Docker Compose
Docker Networking Guide
Bridge, overlay, host networking, DNS, and security
Dockerfile Builder
Generate production-ready Dockerfiles interactively
Docker Compose Validator
Validate and lint Docker Compose YAML files
Docker Commands Cheat Sheet
Essential Docker commands quick reference