July 12, 2026 · Docker HQ Team

How to Fix a Docker Container Stuck in a Restart Loop

A Docker container restart loop means the container starts, crashes within a few seconds, and Docker's restart policy launches it again, on repeat. To fix it, stop the loop, read the container's exit code and its last log lines, and find out why the main process keeps dying. It's almost always a bad command, a missing dependency the container starts before it's ready, a config or permission error, or the process getting killed for using too much memory.

Here's the fast version, then the details.

# 1. See what's looping and why
docker ps -a --filter "name=myapp"
docker inspect myapp --format '{{.State.ExitCode}} {{.State.Error}}'

# 2. Read the logs BEFORE it restarts again
docker logs --tail 100 myapp

# 3. Stop the churn so you can work
docker update --restart=no myapp
docker stop myapp

What causes a Docker container restart loop

Docker doesn't restart containers on a whim. It does it because you told it to, through a restart policy: always, unless-stopped, or on-failure. When the container's main process (PID 1) exits, the Docker daemon sees the container die and starts it again per that policy. If the process keeps dying immediately, you get a loop.

So a restart loop is two problems glued together. The process is crashing, and your restart policy is masking the crash by relaunching it. You have to look past the restart to the underlying exit.

You'll see the loop in docker ps:

CONTAINER ID   IMAGE     STATUS                          NAMES
a1b2c3d4e5f6   myapp     Restarting (1) 3 seconds ago    myapp

That Restarting (1) is the tell. The (1) is the last exit code. If the status flips between Up for a couple seconds and Restarting, that's the loop in motion.

Step 1: Read the exit code

The exit code narrows the cause faster than anything else.

docker inspect myapp --format '{{.State.ExitCode}}'

Common ones:

  • 0: the process exited cleanly. With restart: always Docker will still relaunch it. This is usually a foreground/background mistake: your entrypoint runs something that finishes and returns, instead of staying in the foreground.
  • 1: generic application error. Read the logs; the app threw something on startup.
  • 125: the Docker daemon itself failed to run the container (bad --flag, invalid mount). The container never really started.
  • 126: the command was found but isn't executable (permission bit missing on your entrypoint script).
  • 127: command not found. Wrong path in CMD/ENTRYPOINT, or a binary that isn't in the image.
  • 137: killed by SIGKILL. Almost always the OOM killer: the container hit its memory limit. Confirm with docker inspect myapp --format '{{.State.OOMKilled}}'.
  • 139: segfault (SIGSEGV). Native crash, often an architecture mismatch (an amd64 image on arm64 or vice versa).
  • 143: SIGTERM. The process was asked to stop and did; usually not the loop cause by itself.

Exit 137 and 127 cover a huge share of real loops. One is memory, the other is a typo in your command.

Step 2: Read the logs before they roll

A looping container overwrites its own logs on each restart, so grab them quickly:

docker logs --tail 100 --timestamps myapp

If the container dies too fast to catch anything, follow the logs live and watch the last lines before each exit:

docker logs -f myapp

Watch for the classic startup failures: connection refused to a database, bind: address already in use, permission denied on a mounted path, no such file or directory for a config, or a stack trace on a missing env var. That last line before the crash is your answer most of the time.

Step 3: Stop the loop so you can debug

Trying to debug a moving target is miserable. Kill the restart policy first:

docker update --restart=no myapp
docker stop myapp

Now the container stays dead and you can poke at it without it vanishing under you. docker update changes the policy on a running or stopped container without recreating it, so you don't lose state.

Step 4: Reproduce the crash interactively

The best trick for a container that won't stay up: override the entrypoint and get a shell inside the actual image. Then run the real command by hand and watch it fail with your own eyes.

docker run --rm -it --entrypoint sh myapp
# now inside the container:
ls -la /app
cat /app/config.yaml
./start.sh          # run the real entrypoint manually

If sh isn't in the image, try bash, or for distroless/scratch images you won't get a shell at all. In that case, run the command directly and read stderr:

docker run --rm myapp /path/to/binary --check

Running it by hand shows you the error without the restart policy hiding it.

The usual culprits and their fixes

The process exits because it's not running in the foreground

Docker keeps a container alive only as long as PID 1 runs. If your CMD starts a daemon that forks to the background, PID 1 returns and the container exits 0. Run the process in the foreground instead.

# Wrong: nginx daemonizes, PID 1 exits, container dies
CMD ["nginx"]

# Right: keep it in the foreground
CMD ["nginx", "-g", "daemon off;"]

It depends on a service that isn't ready yet

A container that starts before its database is accepting connections crashes on the first query, restarts, crashes again. depends_on in Compose controls start order, not readiness. Add a real wait or a healthcheck.

services:
  app:
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5

Even better, make the app itself retry the connection on startup instead of assuming the dependency is up. Networks flap; the app should tolerate it.

It's getting OOM-killed (exit 137)

If OOMKilled is true, the process asked for more memory than its limit. Either the limit is too tight or the app is leaking. Raise the limit to confirm, then decide whether the app actually needs it.

docker update --memory=1g --memory-swap=1g myapp

Watch live memory to see whether it climbs steadily (a leak) or spikes at startup (needs a higher ceiling). The docker stats command guide covers reading MEM USAGE / LIMIT and spotting a container walking toward its cap.

A config, secret, or mounted file is missing

Exit codes 126/127 and no such file or directory point here. A volume mount path is wrong, a required env var is unset, or the entrypoint script lost its executable bit after an edit on the host.

# fix the exec bit on an entrypoint script
chmod +x ./entrypoint.sh

# verify the mount actually lands where the app expects
docker inspect myapp --format '{{json .Mounts}}' | jq

Disk is full

A host out of disk space will crash containers in weird ways: databases can't write, logs can't flush, temp files fail. If several unrelated containers start looping at once, check the disk before anything else with df -h and docker system df. Our guide on how to clean up Docker disk usage safely walks through reclaiming space without nuking data you need.

Step 5: Fix, then restore the restart policy

Once the container runs clean by hand, apply the fix to your Dockerfile or Compose file, rebuild, and bring it back with a sensible policy. Prefer on-failure with a cap over blind always so a genuinely broken container can't loop forever.

services:
  app:
    restart: on-failure:5

With on-failure:5, Docker gives up after five failed restarts instead of hammering your host all night. For anything you want back after a reboot, unless-stopped is usually the right default, since it won't override a container you stopped on purpose.

Catching the next one before it wakes you up

Restart loops love to start at 3am, and by the time you SSH in the logs have already rolled past the useful part. A healthcheck plus alerting on restart count is what actually saves the night. This is where Docker HQ helps: it watches your hosts and containers over SSH and sends a push alert the moment a container starts flapping, with the recent logs already pulled so you read the crash from your phone. If you manage more than a couple of hosts, see how it fits your setup. For the fundamentals of what to watch and why, our complete guide to Docker container monitoring is a good next read.

Frequently asked questions

How do I stop a Docker container from restarting?

Change its restart policy to no and stop it: docker update --restart=no <container> followed by docker stop <container>. docker update changes the policy on the existing container without recreating it, so the loop halts and the container stays stopped while you debug.

Why does my container restart even though the app exited successfully?

Because the always restart policy relaunches the container whenever PID 1 exits, including a clean exit 0. This usually means your main process finished instead of staying in the foreground. Run the server in the foreground (for example nginx -g "daemon off;"), or switch to on-failure so a clean exit doesn't trigger a restart.

What does exit code 137 mean in Docker?

Exit 137 means the process was killed by SIGKILL, and in a restart loop it almost always means the OOM killer stopped the container for exceeding its memory limit. Confirm with docker inspect <container> --format '{{.State.OOMKilled}}'. Raise the --memory limit to test, then investigate whether the app is leaking or genuinely needs more.

How can I read logs from a container that crashes instantly?

Run docker logs -f <container> and watch the last lines before each restart, or override the entrypoint to get a shell with docker run --rm -it --entrypoint sh <image> and run the startup command by hand. Running it manually keeps the error on screen instead of letting the restart policy wipe it.

Start with the exit code. It points you at memory, a missing command, or a clean exit that shouldn't be restarting, and that one number saves you from guessing in the logs.