---
# === IDENTITY ===
id: software/debugging/docker-container-wont-start/2026
canonical_question: "How do I debug a Docker container that won't start?"
aliases:
  - "Docker container won't start"
  - "Docker container exited with code"
  - "Docker exit code 1"
  - "Docker exit code 125"
  - "Docker exit code 126"
  - "Docker exit code 127"
  - "Docker exit code 137"
  - "Docker exit code 139"
  - "Docker exit code 143"
  - "Docker ENTRYPOINT not found"
  - "Docker container keeps restarting"
  - "Docker container crash loop"
  - "Docker container stops immediately"
entity_type: software_reference
domain: software > debugging > docker_container_wont_start
region: global
jurisdiction: global
temporal_scope: 2020-2026

# === VERIFICATION ===
last_verified: 2026-05-17
confidence: 0.93
version: 2.1
first_published: 2026-02-17

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "Docker Engine 29.0 (2025-11-10) — API v1.44 required, containerd image store default for fresh installs, private time namespaces enabled by default"
  next_review: 2026-11-13
  change_sensitivity: low

# === CONSTRAINTS ===
constraints:
  - "Always check OOMKilled field (docker inspect --format='{{.State.OOMKilled}}') before assuming exit code 137 means out of memory — docker kill and docker stop timeouts also produce exit 137"
  - "Never mix Shell form and Exec form expectations — CMD node app.js (shell form) wraps in /bin/sh -c and makes the shell PID 1, preventing SIGTERM delivery to the app"
  - "Alpine-based images use musl libc — binaries compiled against glibc (Ubuntu/Debian) will segfault (exit 139) or fail with 'not found' (exit 127) on Alpine"
  - "docker logs only captures stdout/stderr — if the application logs to files inside the container, docker logs shows nothing; reconfigure the app or use docker exec to read log files"
  - "depends_on in Docker Compose (without condition: service_healthy) only waits for the container to start, not for the service to be ready — always pair with healthcheck"
  - "On macOS/Windows, Docker runs inside a Linux VM — memory limits via --memory apply within the VM allocation, not host RAM; check Docker Desktop settings for total VM memory"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Container runs but application behaves incorrectly (wrong output, wrong responses, slow performance)"
    use_instead: "Application-level debugging (not a container startup issue)"
  - condition: "Docker image build fails (docker build returns errors)"
    use_instead: "software/debugging/docker-build-failures/2026"
  - condition: "Kubernetes pod stuck in CrashLoopBackOff"
    use_instead: "software/debugging/kubernetes-crashloopbackoff/2026"

# === AGENT HINTS ===
inputs_needed:
  - key: exit_code
    question: "What is the container's exit code? (Run: docker inspect <container> --format='{{.State.ExitCode}}')"
    type: choice
    options: ["0", "1", "2", "126", "127", "137", "139", "143", "255", "Unknown / no exit code"]
  - key: log_output
    question: "Does 'docker logs <container>' show any error messages?"
    type: choice
    options: ["Yes — error messages visible", "No — logs are empty", "Not sure / haven't checked"]
  - key: orchestrator
    question: "How are you running the container?"
    type: choice
    options: ["docker run (standalone)", "Docker Compose", "Kubernetes", "ECS/Fargate", "Other orchestrator"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/debugging/docker-container-wont-start/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-05-17)"

# === RELATED UNITS ===
related_kos:
  related_to:
    - id: "software/debugging/docker-oomkilled/2026"
      label: "Docker OOMKilled"
    - id: "software/debugging/kubernetes-pod-pending/2026"
      label: "Kubernetes Pod Pending"
  solves:
    - id: "software/debugging/kubernetes-crashloopbackoff/2026"
      label: "Kubernetes CrashLoopBackOff"
  often_confused_with:
    - id: "software/debugging/kubernetes-crashloopbackoff/2026"
      label: "Kubernetes CrashLoopBackOff (orchestrator-level vs docker-level)"
    - id: "software/debugging/nodejs-econnrefused/2026"
      label: "Node.js ECONNREFUSED (app-level vs container-level)"

# === SOURCES (5-8 authoritative sources) ===
sources:
  - id: src1
    title: "Docker — Dockerfile reference (ENTRYPOINT, CMD)"
    author: Docker Inc.
    url: https://docs.docker.com/reference/dockerfile/
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src2
    title: "Docker — docker logs"
    author: Docker Inc.
    url: https://docs.docker.com/reference/cli/docker/container/logs/
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src3
    title: "Docker — docker inspect"
    author: Docker Inc.
    url: https://docs.docker.com/reference/cli/docker/inspect/
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src4
    title: "LogRocket — Docker Container Exit Codes Explained"
    author: LogRocket
    url: https://blog.logrocket.com/docker-container-exit-codes/
    type: technical_blog
    published: 2024-06-01
    reliability: high
  - id: src5
    title: "Komodor — Docker Exit Codes Reference"
    author: Komodor
    url: https://komodor.com/learn/docker-exit-codes-a-complete-guide/
    type: technical_blog
    published: 2025-01-01
    reliability: high
  - id: src6
    title: "HTTP Toolkit — Debugging Docker Containers"
    author: HTTP Toolkit
    url: https://httptoolkit.com/blog/debug-docker-containers/
    type: technical_blog
    published: 2024-06-01
    reliability: high
  - id: src7
    title: "Groundcover — Docker Exit Code 127"
    author: Groundcover
    url: https://www.groundcover.com/blog/docker-exit-code-127
    type: technical_blog
    published: 2024-06-01
    reliability: high
  - id: src8
    title: "Docker Engine v28 and v29 Release Notes"
    author: Docker Inc.
    url: https://docs.docker.com/engine/release-notes/28/
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src9
    title: "Docker Engine 29 Release Notes (v29.0.0, 2025-11-10)"
    author: Docker Inc.
    url: https://docs.docker.com/engine/release-notes/29/
    type: official_docs
    published: 2025-11-10
    reliability: authoritative
---

# How to Debug a Docker Container That Won't Start

## TL;DR

- **Bottom line**: A Docker container that won't start has either a configuration error (wrong ENTRYPOINT/CMD, missing files, bad permissions) or a runtime crash (application error, missing dependency, out of memory). The exit code tells you the category: 1 = app error, 127 = command not found, 137 = OOM/killed, 139 = segfault, 126 = permission denied.
- **Key tool/command**: `docker logs <container>` shows stdout/stderr. `docker inspect <container> --format='{{.State.ExitCode}}'` shows the exit code. `docker run -it --entrypoint /bin/sh <image>` gets you an interactive shell to investigate.
- **Watch out for**: Shell form vs exec form in ENTRYPOINT/CMD — `CMD node app.js` (shell form, runs under `/bin/sh -c`) vs `CMD ["node", "app.js"]` (exec form, runs directly). Shell form can mask exit codes and prevent signal forwarding.
- **Works with**: Docker Engine 20.10+ (including v27, v28, v29), Docker Desktop 4.33+, Podman 4+ (compatible commands). Also applies to containers in Docker Compose v2/v5, Kubernetes, and ECS.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Always check `OOMKilled` field (`docker inspect --format='{{.State.OOMKilled}}'`) before assuming exit code 137 means out of memory -- `docker kill` and `docker stop` timeouts also produce exit 137. [src3]
- Never mix Shell form and Exec form expectations -- `CMD node app.js` (shell form) wraps in `/bin/sh -c` and makes the shell PID 1, preventing SIGTERM delivery to the app. [src1]
- Alpine-based images use musl libc -- binaries compiled against glibc (Ubuntu/Debian) will segfault (exit 139) or fail with "not found" (exit 127) on Alpine. [src5, src7]
- `docker logs` only captures stdout/stderr -- if the application logs to files inside the container, `docker logs` shows nothing. Reconfigure the app or use `docker exec` to read log files. [src2]
- `depends_on` in Docker Compose (without `condition: service_healthy`) only waits for the container to start, not for the service to be ready. Always pair with `healthcheck`. [src4]
- On macOS/Windows, Docker runs inside a Linux VM -- memory limits via `--memory` apply within the VM allocation, not host RAM. Check Docker Desktop settings for total VM memory. [src3]

## Quick Reference

| # | Exit Code | Signal | Meaning | Most Common Cause | Fix |
|---|---|---|---|---|---|
| 1 | 1 | — | Generic error | Application crash, missing env var, config error | Check `docker logs`; fix app code or config [src4] |
| 2 | 2 | — | Shell misuse | Bad shell syntax in entrypoint script | Fix shell script syntax [src4] |
| 3 | 126 | — | Permission denied | Entrypoint/script not executable | `chmod +x entrypoint.sh` in Dockerfile [src5] |
| 4 | 127 | — | Command not found | Binary missing from image, typo in CMD | Check `which <cmd>` inside container; fix Dockerfile [src7] |
| 5 | 137 | SIGKILL (9) | Out of memory / killed | Container exceeded memory limit; OOM killer | Increase `--memory` limit or optimize app [src5] |
| 6 | 139 | SIGSEGV (11) | Segmentation fault | Architecture mismatch (ARM vs x86), C library incompatibility | Build for correct platform; check binary compatibility [src5] |
| 7 | 143 | SIGTERM (15) | Graceful termination | `docker stop` sent SIGTERM; app handled it | Normal shutdown — not an error [src4] |
| 8 | 255 | — | Exit status out of range | Application returned invalid exit code | Fix app to return 0-125 [src4] |
| 9 | 0 | — | Success | Container completed its task | Normal — one-shot container finished [src4] |
| 10 | — | — | No logs, immediate exit | No foreground process; CMD runs in background | Keep process in foreground (no `&`, no daemon mode) [src1] |

## Decision Tree

> Full script: [decision-tree.txt](scripts/decision-tree.txt) (35 lines)

```
START — Container won't start or exits immediately
├── What is the exit code? (docker inspect --format='{{.State.ExitCode}}')
│   ├── Exit 0 → Container finished successfully
│   │   └── Expected for one-shot tasks (migrations, scripts)
│   │   └── If unexpected → CMD/ENTRYPOINT terminates immediately → keep process in foreground [src1]
│   ├── Exit 1 → Application error
│   │   ├── Check docker logs → find the error message [src2]
│   │   ├── Missing env var? → Add -e VAR=value
│   │   ├── Missing file/config? → Check COPY/ADD in Dockerfile [src1]
│   │   └── Dependency error? → Check package installation
│   ├── Exit 126 → Permission denied
│   │   └── chmod +x on entrypoint script; check USER directive [src5]
│   ├── Exit 127 → Command not found
│   │   ├── Typo in CMD/ENTRYPOINT? → Fix Dockerfile [src7]
│   │   ├── Binary not installed? → Add to RUN install [src7]
│   │   └── Wrong base image? → alpine vs debian (musl vs glibc) [src7]
│   ├── Exit 137 → OOM or killed
│   │   ├── OOMKilled=true → Increase --memory or optimize app [src5]
│   │   └── OOMKilled=false → Manual kill or orchestrator [src5]
│   ├── Exit 139 → Segfault
│   │   ├── Architecture mismatch? → Build for correct platform [src5]
│   │   └── musl vs glibc? → Use compatible binaries [src5]
│   └── No exit code / keeps restarting → Check restart policy [src4]
├── Are there any logs?
│   ├── YES → Read: docker logs --tail 50 [src2]
│   └── NO → Run interactively: docker run -it --entrypoint /bin/sh [src6]
└── Multi-stage build issue?
    └── Files missing from final stage → Check COPY --from= [src1]
```

## Step-by-Step Guide

### 1. Check the container status and exit code

Start with `docker ps -a` to see all containers, including exited ones. [src3, src4]

```bash
# List all containers (including stopped)
docker ps -a

# Get the exit code
docker inspect <container_id> --format='{{.State.ExitCode}}'

# Get full state info (exit code, OOMKilled, error, started/finished times)
docker inspect <container_id> --format='{{json .State}}' | python -m json.tool

# Check if OOM killed
docker inspect <container_id> --format='{{.State.OOMKilled}}'

# Docker 29+: include HealthStatus directly in docker ps output
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.HealthStatus}}'
```

**Verify**: You now know the exit code. Use the Quick Reference table above to interpret it. On Docker 29 (released 2025-11-10), the `.HealthStatus` placeholder in `docker ps --format` exposes container health state (`starting`/`healthy`/`unhealthy`) as a first-class field. [src9]

### 2. Read the container logs

Logs show stdout/stderr from the container's main process. [src2]

```bash
# View all logs
docker logs <container_id>

# Last 50 lines
docker logs --tail 50 <container_id>

# Follow logs in real-time (for restarting containers)
docker logs -f <container_id>

# Logs with timestamps
docker logs -t <container_id>

# Logs from a specific time
docker logs --since "2026-02-23T10:00:00" <container_id>
```

**Verify**: Look for error messages, stack traces, "file not found", "permission denied", or "connection refused".

### 3. Use Docker Debug for non-invasive inspection

Docker Debug (GA since Docker Desktop 4.33, free for all users) lets you attach a debugging shell to any container — even stopped or minimal ones with no shell installed. [src8]

```bash
# Attach a debug shell to a running or stopped container
docker debug <container_id>

# Debug with a specific tool image
docker debug --image busybox <container_id>

# Traditional fallback: override entrypoint to get a shell
docker run -it --entrypoint /bin/sh <image_name>

# If the container exited, commit it and debug the snapshot
docker commit <exited_container_id> debug-image
docker run -it --entrypoint /bin/sh debug-image

# If the container is running (restarting), exec into it
docker exec -it <container_id> /bin/sh
```

**Verify**: You can now manually run the ENTRYPOINT/CMD command and see the exact error.

### 4. Inspect the Dockerfile for common mistakes

Review the Dockerfile for configuration errors. [src1]

```dockerfile
# ❌ Common mistakes:

# 1. CMD in shell form (doesn't receive signals properly)
CMD node app.js
# ✅ Use exec form:
CMD ["node", "app.js"]

# 2. Missing chmod on entrypoint script (exit code 126)
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
# ✅ Add chmod:
COPY --chmod=755 entrypoint.sh /entrypoint.sh

# 3. Process runs in background (container exits immediately)
CMD ["nginx"]  # If nginx daemonizes by default
# ✅ Run in foreground:
CMD ["nginx", "-g", "daemon off;"]

# 4. Wrong WORKDIR (exit code 127 — command not found)
WORKDIR /wrong/path
CMD ["./app"]  # File not here!
# ✅ Match WORKDIR to where files are:
WORKDIR /app
COPY . .
CMD ["./app"]

# 5. Multi-stage build missing files
FROM node:20 AS builder
RUN npm run build

FROM node:20-slim
# ❌ Forgot to copy build output:
CMD ["node", "dist/index.js"]
# ✅ Copy from builder:
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
```

**Verify**: Rebuild the image with `docker build -t myapp .` and test again.

### 5. Check environment variables and secrets

Missing environment variables are one of the most common causes of exit code 1. [src4]

```bash
# See what env vars are set in the image
docker inspect <image> --format='{{json .Config.Env}}' | python -m json.tool

# Run with required env vars
docker run -e DATABASE_URL=postgres://... -e SECRET_KEY=... <image>

# Use an env file
docker run --env-file .env <image>

# Docker Compose: check variable substitution
docker compose config  # Shows resolved values
```

**Verify**: Application starts successfully with all required variables set.

### 6. Check resource limits

Exit code 137 usually means the container was killed for exceeding memory limits. [src5]

```bash
# Check container resource usage (live)
docker stats <container_id>

# Check configured limits
docker inspect <container_id> --format='{{.HostConfig.Memory}}'

# Run with higher memory limit
docker run --memory=512m --memory-swap=1g <image>

# Check Docker system resources
docker system info | grep -i memory
docker system df  # Disk usage
```

**Verify**: Container runs without being OOM killed. Monitor with `docker stats`.

### 7. Verify networking and dependencies

Containers that depend on databases or APIs may fail if those services aren't ready. [src4]

```bash
# Check if the container can reach its dependencies
docker run --rm <image> ping -c 3 database-host
docker run --rm <image> nc -zv database-host 5432

# In Docker Compose, use depends_on with health checks
# docker-compose.yml
```

```yaml
services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: postgres://user:pass@db:5432/mydb
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      timeout: 5s
      retries: 5
```

**Verify**: Application starts after dependencies are confirmed healthy.

## Code Examples

### Comprehensive debug entrypoint script

> Full script: [comprehensive-debug-entrypoint-script.sh](scripts/comprehensive-debug-entrypoint-script.sh) (28 lines)

```bash
#!/bin/sh
# Input:  Container that fails with unclear errors
# Output: Detailed diagnostic output before main process starts
set -e
echo "=== Container Debug Info ==="
# ... (see full script)
```

### Docker health check patterns

> Full script: [docker-health-check-patterns.Dockerfile](scripts/docker-health-check-patterns.Dockerfile) (25 lines)

```dockerfile
# Input:  Need to detect unhealthy containers before they affect users
# Output: Health check configurations for common app types
# --- Node.js ---
FROM node:20-slim
COPY . /app
# ... (see full script)
```

### Automated container restart diagnostics

> Full script: [automated-container-restart-diagnostics.py](scripts/automated-container-restart-diagnostics.py) (63 lines)

```python
# Input:  Containers that keep restarting (CrashLoopBackOff-like behavior)
# Output: Script that captures diagnostic info from crashing containers
import subprocess
import json
import time
# ... (see full script)
```

## Anti-Patterns

### Wrong: Shell form CMD (doesn't receive signals)

```dockerfile
# ❌ BAD — runs under /bin/sh -c, PID 1 is shell, not your app [src1]
# App doesn't receive SIGTERM on docker stop → 10s timeout → SIGKILL (exit 137)
CMD node server.js
```

### Correct: Exec form CMD

```dockerfile
# ✅ GOOD — app is PID 1, receives signals directly [src1]
CMD ["node", "server.js"]

# For apps that need shell features (variable expansion):
# Use exec to replace shell process
CMD ["/bin/sh", "-c", "exec node server.js --port=$PORT"]
```

### Wrong: Process daemonizes (container exits immediately)

```dockerfile
# ❌ BAD — nginx forks to background, main process exits, container stops [src1]
CMD ["nginx"]
# Container exits with code 0 but nothing is running
```

### Correct: Run in foreground mode

```dockerfile
# ✅ GOOD — nginx stays in foreground as PID 1 [src1]
CMD ["nginx", "-g", "daemon off;"]

# Or for other apps:
# CMD ["apache2ctl", "-D", "FOREGROUND"]
# CMD ["redis-server", "--daemonize", "no"]
```

### Wrong: No wait-for-dependencies logic

```dockerfile
# ❌ BAD — app starts before database is ready [src4]
# docker-compose.yml
# services:
#   app:
#     depends_on:
#       - db    # Only waits for container to START, not be READY
```

### Correct: Health check + condition

```yaml
# ✅ GOOD — app waits until DB is actually accepting connections [src4]
services:
  app:
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 5s
      retries: 5
```

## Common Pitfalls

- **Shell form vs exec form in CMD/ENTRYPOINT**: Shell form (`CMD node app.js`) wraps your command in `/bin/sh -c`, making the shell PID 1 instead of your app. Your app won't receive SIGTERM on `docker stop`, causing a forced kill after timeout. Always use exec form (`CMD ["node", "app.js"]`). [src1]
- **ENTRYPOINT + CMD interaction**: When both are set, CMD becomes arguments to ENTRYPOINT. If you override CMD at runtime (`docker run image arg1`), it replaces CMD, not ENTRYPOINT. Misunderstanding this causes "exec format error" or wrong arguments. [src1]
- **Alpine linux musl vs glibc**: Binaries compiled for glibc (Ubuntu/Debian) often segfault (exit 139) on Alpine (musl). Either use Alpine-native builds or switch to `*-slim` Debian images. [src5, src7]
- **Multi-stage build missing runtime deps**: Copying only the binary from the build stage but forgetting shared libraries (`.so` files) or CA certificates causes exit 127 or TLS errors. [src1]
- **`docker stop` timeout**: `docker stop` sends SIGTERM, waits 10 seconds, then sends SIGKILL. If your app doesn't handle SIGTERM, it'll always show exit 137 on shutdown. Trap SIGTERM in your entrypoint. [src5]
- **Line endings (Windows to Linux)**: Shell scripts created on Windows often have `\r\n` line endings. Linux reads `\r` as part of the command, causing "command not found" errors. Use `dos2unix` or configure your editor. [src7]
- **Compose v5 Go SDK changes**: Docker Desktop 4.38+ ships Compose v5 with a new Go SDK. Build configurations that worked with Compose v2 may need updating for Bake-based workflows. [src8]

## Diagnostic Commands

> Full script: [diagnostic-commands.sh](scripts/diagnostic-commands.sh) (33 lines)

```bash
# === Container Status ===
docker ps -a                                          # All containers
docker inspect <cid> --format='{{.State.ExitCode}}'   # Exit code
docker inspect <cid> --format='{{.State.OOMKilled}}'  # OOM killed?
docker inspect <cid> --format='{{json .State}}'       # Full state JSON

# === Logs ===
docker logs <cid>                                     # All logs
docker logs --tail 50 <cid>                           # Last 50 lines
docker logs -f <cid>                                  # Follow (live)

# === Docker Debug (Docker Desktop 4.33+) ===
docker debug <cid>                                    # Non-invasive debug shell

# === Interactive Debugging (fallback) ===
docker run -it --entrypoint /bin/sh <image>           # Override entrypoint
docker exec -it <cid> /bin/sh                         # Exec into running
docker commit <cid> debug && docker run -it --entrypoint /bin/sh debug

# === Image Inspection ===
docker inspect <image> --format='{{json .Config.Entrypoint}}'
docker inspect <image> --format='{{json .Config.Cmd}}'
docker history <image>

# === Resources ===
docker stats <cid>
docker system df

# === Restart Policy ===
docker inspect <cid> --format='{{.HostConfig.RestartPolicy.Name}}'
docker update --restart=no <cid>

# === Docker Compose ===
docker compose logs <service>
docker compose config
docker compose ps
```

## Version History & Compatibility

| Version | Behavior | Key Changes |
|---|---|---|
| Docker 29 (2025-11-10) | Current | `docker ps --format '{{.HealthStatus}}'` placeholder for health state; private time namespace default; runtime support on Windows; local log driver custom attributes; CLI plugin error hooks; API v1.44 minimum [src9] |
| Docker 28 (2025) | Stable | Networking improvements, NRI support; BuildKit v0.23, Buildx v0.25, Compose v2.37 [src8] |
| Docker 27 (2024) | Stable | Containerd image store default; kernel module loading improvements [src8] |
| Docker 25+ (2024) | Stable | `docker init` for Dockerfile generation; improved build cache [src1] |
| Docker 24 | Stable | BuildKit default; containerd image store option [src1] |
| Docker 23 | Stable | Docker Compose V2 default (docker compose, not docker-compose) [src1] |
| Docker 20.10 | LTS-like | cgroups v2 support; --platform flag for multi-arch [src1] |
| Docker Desktop 4.33+ | GA | `docker debug` free for all users — debug any container non-invasively [src8] |
| Docker Desktop 4.38+ | Current | Bake GA, Docker AI Agent, multi-node K8s testing [src8] |
| Podman 4+ | Drop-in replacement | Same CLI commands; rootless by default [src3] |

## Decision Logic

### If exit code is 1 and `docker logs` shows a stack trace
--> Application crashed. Read the trace bottom-up to find the root cause (missing env var, bad config, unhandled exception). Fix the app code or supply the missing input. [src2, src4]

### If exit code is 127 and the command appears correct
--> Either binary is missing from the image (typo, not installed) or the base image is wrong (Alpine musl vs glibc binary). Run `docker run -it --entrypoint /bin/sh <image>` and `which <command>` to confirm. [src5, src7]

### If exit code is 137 and `OOMKilled=true`
--> Container exceeded its memory limit. Increase `--memory` (or `mem_limit` in Compose) or profile and reduce the app's heap usage. On Docker Desktop, also verify the Linux VM has enough RAM allocated. [src3, src5]

### If exit code is 137 and `OOMKilled=false`
--> A `docker stop` SIGTERM was sent and the app didn't exit within the grace period (default 10s), so SIGKILL was issued. Add a SIGTERM signal handler in the app, or extend the grace period with `--stop-timeout`. [src1, src5]

### If exit code is 139 (segfault) on Apple Silicon, ARM, or after a base-image swap
--> Architecture or libc mismatch. Rebuild with `docker build --platform linux/amd64` (or the target arch), or switch from `alpine` (musl) to `*-slim` (glibc) base. [src5, src7]

### If `docker logs` is empty but container exits immediately
--> Either the app logs to a file inside the container (reconfigure to stdout/stderr) OR the CMD daemonizes (e.g. `nginx`, `apache2ctl` without foreground flag). Re-run with `docker debug` (Desktop 4.33+) or override the entrypoint to inspect. [src1, src2, src8]

### If container starts then exits with code 0 immediately
--> The main process completed and exited (expected for one-shot containers like migrations). If unintended, the CMD is forking to background — keep the process in foreground with `daemon off;`, `-D FOREGROUND`, or similar. [src1]

### If Docker Compose service starts before its database is ready
--> `depends_on` alone only waits for container start, not readiness. Add a `healthcheck` to the dependency and use `depends_on: db: condition: service_healthy` (Compose v2+). [src4]

### If you can't get a shell because the image is distroless/scratch
--> Use `docker debug <container>` (Docker Desktop 4.33+ GA) — it attaches a debug toolbox to even stopped or shell-less containers without modifying them. Otherwise `docker commit` the exited container and run an interactive shell against the snapshot. [src6, src8, src9]

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Container exits with non-zero code | Container runs but app behaves wrong | Application-level debugging |
| Container loop-restarts | Image builds fail | `docker build --no-cache` + read build output |
| Container starts but crashes after seconds | Network-level issues between containers | `docker network inspect` + DNS debugging |
| docker compose up fails for one service | Orchestrator-level issues (K8s) | kubectl describe pod / kubectl logs |
| Exit code appears but logs are empty | Docker Desktop itself won't start | Docker Desktop troubleshooting docs |

## Important Caveats

- `docker logs` only shows stdout/stderr. If your application logs to a file inside the container, `docker logs` will show nothing. Configure your app to log to stdout/stderr, or use `docker exec` to read log files.
- Exit code 137 doesn't always mean OOM — it can also mean someone ran `docker kill` or `docker stop` timed out. Check the `OOMKilled` field in `docker inspect` to distinguish.
- `docker stop` sends SIGTERM first, then SIGKILL after the grace period (default 10s). If your app always exits with code 137, it's not handling SIGTERM. Add a signal handler or use `--stop-timeout`.
- On macOS/Windows, Docker runs inside a Linux VM. Memory limits set with `--memory` are within the VM's allocation, not the host. Check Docker Desktop settings for the VM's total memory.
- `docker commit` captures the container's filesystem but not volumes, network configuration, or running processes. It's useful for debugging but not for production image creation.
- When using `depends_on` in Docker Compose, the default behavior only waits for the dependent container to *start*, not to be *ready*. Use `condition: service_healthy` with a `healthcheck` for proper startup ordering.
- `docker debug` (Docker Desktop 4.33+) can attach to stopped containers and minimal images (distroless/scratch) — it injects a debug toolbox without modifying the target container. Free for all Docker users since late 2024.

## Related Units
<!-- Generated from related_kos frontmatter -->

- [Docker OOMKilled](/software/debugging/docker-oomkilled/2026)
- [Kubernetes Pod Pending](/software/debugging/kubernetes-pod-pending/2026)
- [Kubernetes CrashLoopBackOff](/software/debugging/kubernetes-crashloopbackoff/2026)
- [Node.js ECONNREFUSED](/software/debugging/nodejs-econnrefused/2026)
