Skip to content

How to Fix a Docker Container That Keeps Restarting

A container stuck in a restart loop? Read the logs, check the exit code, and fix the real cause: bad config, missing files, port conflicts, or dependencies.

SDSysadmin Desk October 17, 2026 7 min read
Photographic illustration for How to Fix a Docker Container That Keeps Restarting: server racks with glowing container blocks

A container that keeps restarting looks alarming in docker ps — the status flickers between Restarting and Up (about a second) and never settles. The instinct is to blame the restart policy and turn it off. That’s almost never the fix.

A restart policy of always or unless-stopped does exactly one thing: relaunch the container whenever its main process exits. If the process crashes the moment it starts, Docker dutifully restarts it, it crashes again, and you get a loop. The policy is doing its job. The container is failing, and the reason is in the logs.

This guide is the diagnostic path: confirm the loop, read the logs, check the exit code, and fix the actual cause. The same steps work whether you started the container with docker run or with Compose.

Confirm what you’re looking at

First, see the status and how the container is behaving:

docker ps -a
CONTAINER ID   IMAGE        STATUS                          NAMES
a1b2c3d4e5f6   my-app:1.0   Restarting (1) 4 seconds ago    app

Restarting (1) is the tell. The number in parentheses is the exit code the process returned before Docker restarted it — here, 1. A container that exits and gets relaunched over and over is in a crash loop. If the status is constantly cycling, that’s your confirmation.

Read the logs — this is where the answer lives

The single most useful command. It shows what the process printed before it died, even though the container isn’t running right now:

# Everything the container has logged
docker logs app

# Just the last 50 lines, which is usually where the crash is
docker logs --tail 50 app

# For a Compose stack, by service name
docker compose logs --tail 50 app

Most of the time the answer is sitting right there: a config file it couldn’t parse, a database it couldn’t reach, a missing environment variable, a port it couldn’t bind. Read the last 10–20 lines carefully before you change anything.

Check the exit code

The exit code narrows the cause fast. Pull it with docker inspect:

docker inspect app --format '{{ .State.ExitCode }} {{ .State.Error }}'

Here’s what the common codes mean:

Common container exit codes

Exit code Usual meaning
0 Clean exit. The process finished and isn't a long-running service (check your command).
1 Generic application error. Read the logs for the specific failure.
125 Docker itself couldn't run the container (bad run options or flags).
126 The command was found but isn't executable (permissions or wrong format).
127 Command not found — wrong path in the entrypoint or command.
137 Killed by SIGKILL — usually out of memory (OOM) or a stop that timed out.
143 Terminated by SIGTERM — a normal stop signal.

Two of these come up constantly. Exit 137 points at memory — the host ran out, or a container memory limit was hit and the kernel killed the process. Exit 0 on a service that’s supposed to stay running usually means the command does its thing and exits, like running an interactive image with no foreground process; the container has nothing to keep alive, so it stops, and the policy restarts it into the same dead end.

The usual root causes

Once you’ve read the logs and noted the exit code, it’s almost always one of these.

Bad or missing configuration

A malformed config file or a missing required environment variable kills many apps on startup. The logs typically name the file or the variable. Fix the value, then recreate:

docker compose up -d   # recreates the container with corrected config

If you’re managing config through environment variables, a missing or empty value is a frequent culprit — the environment variables in Compose guide covers why a variable can end up empty inside a container.

A missing or wrong mount

If the app expects a file or directory that isn’t mounted — a config file, a certificate, a data directory — it crashes looking for it. Check that your bind mounts point at paths that exist on the host and land where the app expects inside the container. Confirm the mounts Docker actually applied:

docker inspect app --format '{{ json .Mounts }}'

A port conflict

If the app can’t bind its port because something else already owns it, it may exit on startup. This shows up as a bind error in the logs. The fixes are covered in port already allocated and bind address already in use — usually you change the host-side port or stop whatever’s holding it.

A dependency that isn’t ready

A web app that connects to a database on startup will crash if the database isn’t accepting connections yet. depends_on controls start order, but by default it doesn’t wait for the dependency to be ready. Add a healthcheck to the dependency and use the long-form depends_on so the app waits properly:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: change-me
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 5

  app:
    image: my-app:1.0
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

Now app doesn’t start until Postgres reports healthy, which removes the “database not ready” crash on first boot.

Out of memory

If docker inspect shows exit code 137 and the logs cut off abruptly, the process was likely OOM-killed. Check host memory with free -h, and if you set a memory limit on the container, confirm it’s high enough for the app. Raising or removing an over-tight limit, or freeing host memory, stops this class of restart.

Stop the loop while you work

A constantly restarting container is noisy and can churn CPU. Pause the loop so you can investigate calmly:

# Switch the policy off and stop it
docker update --restart=no app
docker stop app

# With Compose, set restart: "no" (or remove the line) then:
docker compose up -d

The container now stays stopped. Fix the root cause, set the policy back to unless-stopped, and bring it up again.

Restart-loop diagnostic order

  • docker ps -a — confirm the Restarting status and note the exit code
  • docker logs --tail 50 — read the real error
  • docker inspect — check exit code, configured command, and mounts
  • Match the cause: config, missing mount, port conflict, dependency, or OOM
  • For dependency timing, add a healthcheck and condition: service_healthy
  • Fix the cause, then restore restart: unless-stopped

When the logs are empty

Sometimes the container dies before the app prints anything, leaving an empty log. That usually means the entrypoint or command is wrong, or the executable isn’t there (watch for exit codes 126 and 127). Run the image interactively with a shell to poke around without the loop:

docker run --rm -it --entrypoint sh my-app:1.0
# then try to start the app by hand and watch what happens

If the daemon itself is unreachable rather than a single container failing, that’s a different problem — start with fixing cannot connect to the Docker daemon.

A restart loop is rarely mysterious once you stop treating the policy as the villain. Read the logs, read the exit code, fix the cause underneath. Nine times out of ten the container is telling you exactly what’s wrong — you just have to look at what it printed on the way down.

Frequently asked questions

Why does my container keep restarting?

A restart policy of always or unless-stopped relaunches a container every time its main process exits. If the process crashes immediately on start — from bad config, a missing file, a port conflict, or a dependency that isn't ready — Docker keeps restarting it. The policy is working; the container is failing. The logs hold the real reason.

How do I see why a container crashed?

Run docker logs to read what the process printed before it died, even though the container isn't running now. Add --tail 50 to see just the last lines. For Compose, use docker compose logs . The error message is almost always there.

What does exit code 137 mean?

137 means the process was killed by SIGKILL, most often because it ran out of memory and was OOM-killed, or because a stop timed out. Check the host's memory and any memory limit on the container. Exit code 1 is a generic application error; 126/127 mean a command wasn't executable or wasn't found.

How do I stop the restart loop while I investigate?

Change the policy temporarily so it stops relaunching: docker update --restart=no then docker stop . With Compose, set restart: no (or remove the line) and run docker compose up -d. Now the container stays stopped so you can inspect it without the loop.

The logs are empty — now what?

An empty log usually means the container died before the app produced output, often a bad entrypoint or command, or a missing executable. Run docker inspect to read the exit code and the configured command, and try running the image interactively with an overridden command to see what happens.

Sources & further reading

Official vendor documentation referenced while writing this guide.

SD

Sysadmin Desk

Infrastructure & Cloud

Hands-on guidance for infrastructure, virtualization, and containers — Hyper-V, VMware, Docker, and the day-to-day operations work that keeps environments running.

MCSA Guru provides independent, educational IT guidance. Microsoft, Windows, Windows Server, Microsoft 365, Exchange, and Microsoft Teams are trademarks of Microsoft Corporation; Docker is a trademark of Docker, Inc. MCSA Guru is not affiliated with or endorsed by Microsoft or Docker. Always test changes in a safe environment before applying them in production.

Related guides

Fixing something right now?

Jump straight into the guide library or search for the exact error or task you are dealing with.