Common Docker Errors and Fixes: The Practical Field Guide
Read the exit code first: 137 = killed (OOM), 1 = app error, 127 = command not found. Then read the logs — the container itself almost never lies.
Container Exits Immediately
docker logs <container> # the app's own message
docker inspect <container> --format='{{.State.ExitCode}}'| Exit code | Meaning | Typical fix |
|---|---|---|
| 0 | Command finished (fine for batch, broken for servers) | The entrypoint process ended — a server must stay in foreground |
| 1 | Application error | Read the logs; often missing env vars or config |
| 126 | Permission denied executing the command | chmod +x the entrypoint script |
| 127 | Command not found | Wrong path, or binary not installed in the image |
| 137 | Killed — SIGKILL: OOM or docker stop | Raise memory limit, or find the leak |
| 143 | SIGTERM — graceful stop | Normal shutdown, usually not an error |
The classic 0-exit server case: a CMD/entrypoint that daemonizes (nginx without -g "daemon off;", or a shell script ending in &) — the container's main process exits and the container follows, even though the daemon inside is (briefly) running.
"bind: address already in use"
docker run -p 3000:3000 myapp
# Error: bind: address already in useThe host port is taken — by another container, or a local process:
lsof -i :3000 # macOS/Linux: who holds it
docker ps -a # any old container publishing 3000?Fixes: stop the old container (docker stop); or map a different host port (-p 3001:3000 — container port unchanged); or find the local process and stop it. On macOS, note that Docker Desktop and a local dev server can fight over the same port.
COPY Failed: No Such File or Directory
COPY package.json ./
# ERROR: failed to compute cache key: "/package.json": not foundThree causes, in order of frequency: (1) the path is outside the build context — the build context is the directory you pass to docker build (usually .), and COPY can only read from within it. Building from the wrong directory produces this instantly. (2) .dockerignore excludes the file — check with docker build --no-cache after reading the ignore file; a pattern like *.md or a miswritten node_modules line can shadow real files. (3) Case sensitivity: COPY Package.json works on macOS, fails on Linux builders — match the case exactly.
Permission Denied on Volumes (Linux)
docker run -v $(pwd)/data:/app/data myapp
# app: permission denied: /app/dataWhy: the container runs as a non-root user (or root), and the host directory's ownership doesn't match the UID inside. Fixes, best to worst: (1) set the runtime user to match the host owner: --user $(id -u):$(id -g); (2) build the image with a known user and chown the directory: chown -R 1000:1000 ./data; (3) last resort, run as root — works, hides the mismatch, keeps root-owned files on your host.
On macOS/Windows with Docker Desktop, bind mounts go through a VM and masking usually prevents this; the Linux-server environment is where it bites.
Image Pull Failures
| Error | Cause | Fix |
|---|---|---|
| manifest unknown / not found | Tag doesn't exist (typo, or tag was deleted) | Check the registry tags page; use a digest for pinning |
| unauthorized / denied | Not logged in, or missing repo permission | docker login registry; verify the token scope |
| net/http: TLS handshake timeout | Network/proxy/DNS in the daemon context | Configure daemon proxy; retry — large pulls also just time out sometimes |
| no space left on device | Disk full from layers/cache | docker system prune -a (frees images+cache), docker volume prune (careful: volumes hold data) |
| toomanyrequests (rate limit) | Docker Hub anonymous pull limit | Log in (higher limit), or mirror the image to your own registry |
The Build Cache Trap
"It works locally, fails in CI" is usually layer ordering: put dependency installation (COPY package.json + install) before copying source, so code changes don't invalidate the dependency layer. And when a build behaves impossibly, docker build --no-cache establishes ground truth before you debug the cache itself.
A Debugging Habit That Saves the Most Time
When a container fails at startup, override the entrypoint and stand inside it: docker run -it --entrypoint sh myimage — then run the startup commands manually, one at a time. Every "works in shell, fails in Docker" mystery becomes a step-by-step comparison: the environment, the working directory, the user, the PATH. That comparison IS the diagnosis.