Blog
4 min read

Docker Container Keeps Restarting: How to Find Out Why

A container stuck in 'Restarting' is crashing on start and being brought back by its restart policy. How to read exit codes (1, 126, 127, 137, 139, 143), get logs from a crash loop, override the entrypoint to look inside, and fix the usual causes: config errors, OOM kills, missing dependencies and failing healthchecks.

$ docker ps
CONTAINER ID   IMAGE    STATUS                          NAMES
4f2a…          myapp    Restarting (1) 12 seconds ago   web

The container's main process is exiting, and its restart policy (restart: always, unless-stopped, on-failure) keeps starting it again — a crash loop. Docker increases the delay between restarts, which is why it sometimes looks stuck. The job is to find out why the process exits.

Step 1: read the logs

docker logs --tail 100 web
docker compose logs --tail 100 web

Logs from previous runs are kept, so you'll see the error even if the container is between restarts. Nine times out of ten the cause is right there: a stack trace, Error: Cannot find module, DATABASE_URL is not set, connection refused. (How to read an error message)

Step 2: check the exit code and reason

docker inspect web --format '{{.State.ExitCode}} {{.State.OOMKilled}} {{.State.Error}}'
Exit code Usually means
0 The process finished normally — but a server shouldn't finish. It ran a one-off command, or daemonised itself into the background
1 Application error — check the logs
126 Command found but not executable (permissions)
127 Command not found — wrong CMD, missing binary, PATH (Command not found)
137 Killed with SIGKILL — very often out of memory (OOMKilled: true)
139 Segmentation fault — native code crash, often wrong architecture or library
143 SIGTERM — something asked it to stop (orchestrator, healthcheck failure, deploy)

The usual causes

Configuration and environment

Missing environment variables, a wrong database URL, a typo in a config file. The app reads config at start-up and exits. Check what the container actually received:

docker inspect web --format '{{json .Config.Env}}'

(Environment variables explained)

A dependency isn't ready

The app tries the database on start, it's not accepting connections yet, the app exits, repeat. Add a healthcheck on the database with depends_on: condition: service_healthy, and make the app retry its connection. (Docker Compose networking)

Out of memory (exit 137)

The container exceeded its memory limit, or the host ran out. Check docker stats, raise the limit, or fix the leak. For Node.js, set --max-old-space-size below the container limit. (Container CPU and memory limits, Linux OOM killer, Node memory leaks)

Wrong command or architecture

Exit 127 or exec format error — the CMD points at something that doesn't exist, or the image was built for a different CPU. (exec format error)

The process exits immediately (exit 0)

A container lives only as long as its main process. If CMD starts a service that forks into the background (service nginx start, pm2 start without pm2-runtime), the main process ends and so does the container. Run the server in the foreground: nginx -g 'daemon off;', pm2-runtime, node server.js.

Permissions

The app can't write to a mounted volume owned by another user, or bind to a port below 1024 as non-root. (Linux file permissions)

A failing healthcheck (with orchestration)

Under Swarm, Kubernetes or some platforms, a container failing its healthcheck is killed and replaced (exit 143). The app may be fine but the check is wrong — wrong port, too short a start period. (Health check endpoints)

Step 3: get inside a crash-looping container

You can't docker exec into something that keeps dying. Stop the loop and start it with a shell instead of the normal command:

docker compose stop web
docker compose run --rm --entrypoint sh web
# or
docker run --rm -it --entrypoint sh myapp

Now run the start command by hand and watch it fail, check files exist, test the database connection.

Or temporarily keep it alive to inspect:

command: ["sleep", "infinity"]

Step 4: stop the noise while you fix it

docker update --restart=no web

Prevent the next one

  • Validate required config at start-up with a clear error message.
  • Retry dependencies with backoff instead of exiting. (Exponential backoff)
  • Set memory limits deliberately and monitor them.
  • Alert on restart counts — a container restarting every few minutes can look "up" to a simple check. (Know when your app is down)

EasySpawn runs your containers with logs and restart history in one place, and Claude Code can read the crash, override the entrypoint and find the cause for you. See how it works or join the waitlist.

Related: Docker Commands Cheat Sheet · How Container CPU and Memory Limits Work · PID 1 in Containers · 502 Bad Gateway

Keep reading