Skip to main content
+44 7575 472931[email protected]
HostaccentKnowledge BaseHosting, websites, SEO, and growth

Docker Container Keeps Restarting? Find the Real Cause

Docker container keeps restarting in a loop? Read the exit code, find the real cause in minutes, and fix OOM kills, bad entrypoints and failing health checks.

VPSWeb Hosting
Docker container keeps restarting in a loop, with exit code 137 and crash logs on a Linux VPS terminal, 2026

Your docker ps output says Restarting (1) 3 seconds ago. You run it again. Same line, different number. If your docker container keeps restarting like this, nothing is recovering. The container is dying, the restart policy is resurrecting it, and every cycle wipes the evidence you need to work out why.

Quick Answer (as of August 2026): A container restarts in a loop because its main process exits and the restart policy starts it again. Check the exit code first with docker inspect. Code 137 with OOMKilled: true means memory. Code 1 means the app crashed. Codes 126 and 127 mean a broken entrypoint. Fix the cause, never the policy.

Our engineers work through 20 to 30 client server issues every day, and container crash loops land in that queue most weeks. What follows is the way our engineers at Hostaccent triage it, written so you can run the whole thing yourself in about ten minutes, without a rebuild and without deleting anything.

You will get the exact commands, the exit code map, the five causes ranked by how often they actually happen, and the two host-level causes that almost nobody writes about.

What "Docker Container Keeps Restarting" Actually Means

The loop is not a Docker bug. It is Docker doing exactly what you told it to do. Your container's main process (PID 1 inside the container) exits, the daemon notices, and the restart policy you set with --restart starts a fresh instance. The app crashes again, and the cycle repeats.

Docker tracks this in RestartCount, which survives daemon restarts, and it adds an exponential back-off between attempts that caps at roughly one minute. That back-off is why a container can look like it settled down when it is still cycling quietly in the background. Check the counter, not your gut feeling about whether it stopped.

There are four policies, and the difference matters: no (the default), on-failure, always, and unless-stopped. The always setting restarts on any exit, including a clean exit code 0, which is exactly what turns a one-shot migration script into an infinite loop. Docker's restart policy documentation covers the behaviour in detail, including one rule people miss: a policy only kicks in once the container has started successfully, meaning it stayed up for at least 10 seconds.

Freeze the loop before you touch anything

Your first command should stop the churn without killing the container or destroying its state:

bash
docker update --restart=no my-container

The container keeps running until it fails on its own, and then it stays down. Now you can read logs, inspect the filesystem, and think, instead of racing a process that restarts every four seconds. This is the single highest-value command in the whole article.

Do not confuse this with a fix. Turning the policy off stops the symptom and leaves the disease. The rest of this guide is about the disease.

If you are setting this host up from scratch, or you inherited a machine with an unclear Docker install, the Install Docker on Ubuntu VPS: Secure Production Setup guide covers the daemon configuration that prevents half these problems before they start.

Step One: Read the Exit Code Before You Change Anything

Almost everyone gets this backwards. They rebuild the image, change the base tag, add sudo, restart the daemon, and only later look at the exit code that told them the answer in the first second. Read the code first. Every time.

Here is the triage we run, and it takes under a minute. Call it the 60-Second Loop Triage: three commands, in this order, before you form a single theory.

bash
# 1. The verdict: exit code plus the OOM flag in one line
docker inspect my-container --format '{{.State.ExitCode}} {{.State.OOMKilled}} {{.RestartCount}}'

# 2. The confession: what the app said on its way out
docker logs my-container --tail 100 --timestamps 2>&1

# 3. The timeline: how long it survived each attempt
docker inspect my-container --format '{{.State.StartedAt}} {{.State.FinishedAt}}'

Command three is the one people skip, and it is the fastest way to split the possible causes in half. A container that dies inside 1 second is failing at startup: missing file, missing variable, broken command. A container that runs for 30 seconds and then dies is failing on a dependency timeout or a health check.

What each exit code actually tells you

| Exit code | Signal or meaning | Real cause in most cases | |---|---|---| | 0 | Clean exit | The process finished its job. A job-like container under an always policy. | | 1 | Application error | Unhandled exception, missing config, failed startup check. | | 126 | Command not executable | Entrypoint script without the execute bit set. | | 127 | Command not found | Wrong path in CMD, or a binary missing from a slimmer base image. | | 137 | SIGKILL (128 + 9) | Memory limit hit, if OOMKilled: true. Otherwise something else sent a hard kill. | | 139 | SIGSEGV (128 + 11) | Native crash. Bad binary, wrong architecture, corrupted library. | | 143 | SIGTERM (128 + 15) | Something asked it to stop. In a loop, that usually means a health check or a supervisor. |

One detail worth burning into memory: exit code 137 does not automatically mean out of memory. It means SIGKILL. If OOMKilled reads false, the kill came from somewhere else, and you should be looking at your orchestrator, a watchdog script, or a docker stop timeout that expired while your app was still shutting down.

Is it my app, or is it Docker?

Empty logs are the tell. If docker logs returns nothing at all across several restart cycles, your application never reached the point of writing output, which means the failure happened before your code ran: a bad entrypoint, a missing mount, a permissions problem on a volume, or an architecture mismatch between the image and the host. If the logs are full of stack traces or connection errors, the failure is inside your application and Docker is an innocent bystander.

Pro Tip: Always add --timestamps to docker logs during a crash loop. Without it, several restart cycles blur into one stream and you cannot tell which error belongs to which attempt. With it, the repeating pattern becomes obvious in about five seconds.

The Causes We See Most, Ranked by How Often They Bite

Ranked by how often they turn up in real incidents, not by how interesting they are.

1. The process was never meant to keep running. Your CMD runs a migration, a build step, or a shell script that finishes. It exits 0, the always policy restarts it, and you have an infinite loop of a successful job. This is the most common docker container crash loop we see in application containers, and it is also the one people diagnose last, because "everything worked" is a strange thing to see in a broken system.

2. A missing environment variable or config file. The container exits immediately with code 1 and a one-line error nobody reads. A .env file that lives in your local directory but never made it to the server accounts for a surprising share of these.

3. The memory ceiling. Exit code 137, OOMKilled: true. Either the container limit is too tight, or the host is out of RAM and the kernel picked your container as the sacrifice.

4. A dependency that is not ready. The app boots faster than the database, gets connection refused, exits, restarts, and races the same startup again. Under Compose, depends_on alone does not wait for readiness, only for the container to exist.

5. A health check fighting the container. A check with a short start_period marks a slow-starting app unhealthy before it finishes booting, and your orchestrator replaces it. The container was fine. The check was wrong.

From the Ticket Queue: According to Hostaccent's support-queue data (2026), Linux server issues account for roughly 25% of the tickets we handle every month, alongside WordPress problems at 30%, brute-force and malware at 25%, and SSL at 20%. Container restart loops sit inside that Linux slice, and the split we see is consistent: most are configuration or memory, not application bugs.

Across the 4,000+ sites our team has migrated since 2016, there is one pattern worth stealing. An image that ran happily on a 16GB laptop meets a 2GB VPS and dies on first contact, usually during the heaviest part of startup, before the app has trimmed its working set. The image did not change. The ceiling did.

Professional help available

Still working through this server issue?

Send the symptoms, error output, and what you have already tried. We can work with Hostaccent services or infrastructure hosted with another provider.

Request server helpHow server support worksHosted elsewhere? One-time paid support is available after scope and price confirmation.

Fix Each Cause: Commands, Files and Config That Work

Work down this list in the order your exit code points to. Change one thing, then observe. Changing three things at once is how a fifteen-minute fix turns into an afternoon.

Fix 1: the container that finished its job

If your exit code is 0 and the container is short-lived by design, the policy is wrong, not the app. Use on-failure for job-like containers so a clean exit is respected:

bash
docker update --restart=on-failure:3 my-migration

For a genuine long-running service, make sure the process stays in the foreground. A daemonised process inside a container is a container with nothing left to supervise. If you are running Node behind a process manager, the pattern in Deploy Node.js App on Linux VPS: PM2 + Nginx Beginner Guide shows the foreground setup that avoids this trap.

Fix 2: missing configuration

Get inside the image without letting it run its broken command:

bash
docker run -it --rm --entrypoint /bin/sh my-image:latest

Now check what the app expects. Print the variables the container actually received with docker inspect my-container --format '{{json .Config.Env}}' and compare that against your .env. Confirm the file is where you think it is, and that the container user can read it. In Compose, verify the env_file path is relative to the Compose file, which is not always the directory you ran the command from.

Fix 3: the memory ceiling

Confirm the kill before you spend money on RAM:

bash
docker inspect my-container --format '{{.State.OOMKilled}}'
dmesg -T | grep -i "killed process"

If the kernel log shows the kill, decide which ceiling moved: the container limit or the host. Set an explicit limit rather than leaving it unbounded, because an unbounded container can take the whole host down with it, including every other service on the box. Docker's memory and CPU constraint reference covers the flags in full.

yaml
services:
  app:
    image: myapp:latest
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512M

Match the limit to the application's own configuration. A JVM heap or a PHP-FPM pool sized larger than the container limit will get killed every time, no matter how many times you restart it. Our Nginx + PHP-FPM Performance Tuning on Linux VPS guide covers the pool arithmetic that keeps those two numbers in agreement.

Live site down and no time to experiment? Our engineers fix this exact class of failure for a small one-time fee, and you see the exact quote before anyone touches your server. Hosted with Hostaccent? Then issues like this are simply covered by support, at no extra charge. Have an engineer fix it

Fix 4: dependencies and health checks

Make the app wait for a dependency that is genuinely ready, not merely present:

yaml
services:
  db:
    image: mariadb:11
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
  app:
    image: myapp:latest
    depends_on:
      db:
        condition: service_healthy

If the health check itself is the culprit, widen start_period before you touch anything else. That window exists so a slow boot does not count as a failure.

Insider Insight: interval and start_period do different jobs, and mixing them up causes more false restarts than any other Compose setting. start_period is the grace window during which failed checks do not count against you. If your app needs 45 seconds to warm a cache, set start_period: 60s and leave interval short so you still get fast detection afterwards.

Confirm the Fix and Keep the Loop From Coming Back

A container that has been up for two minutes has proven nothing. Verify properly.

bash
docker inspect my-container --format '{{.RestartCount}} {{.State.Status}} {{.State.StartedAt}}'

Recreate the container so the counter resets from a clean baseline, then check that number again after a few hours and again the next morning. A slow memory leak takes hours to show up, and a counter that reads 4 the following day is telling you the problem moved rather than left. The docker container restart CLI reference and the docker compose restart reference cover the behaviour differences between restarting a container and recreating it, which matter here.

Then close the two host-level doors that cause loops nobody can explain from inside the container.

Disk pressure. Run df -h and docker system df. When /var/lib/docker fills, containers fail to write and crash in ways that look exactly like application bugs. Unrotated JSON logs are the usual offender, so cap them in /etc/docker/daemon.json:

json
{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

Blind spots. You should learn about a restart from a monitor, not from a customer. Setting up server monitoring on your VPS with an alert on container restarts and host memory turns this class of incident from an outage into a notification. Pair it with working backup automation using rsync and cron so a bad image rollback is a five-minute job, and with a proper Linux VPS security baseline, because a compromised container that gets killed repeatedly looks identical to a crash loop until you check.

Four things to carry away from this:

  • Read the exit code before you change anything, and read StartedAt to see whether it died in 1 second or 30.
  • Exit code 137 means SIGKILL. Only OOMKilled: true makes it a memory problem.
  • Turning off the restart policy buys you time to investigate. It is not a fix.
  • Set a memory limit and a sensible start_period on every service, then check RestartCount the next day.

Your Next Step After the Loop Stops

Fixed it yourself? Keep one habit: a memory limit and a start_period on every service you deploy, checked once the morning after. That one look catches leaks before your visitors do.

Still stuck, or watching this happen on a live site at 2am? Two honest routes. Open a ticket and our engineers will take it, small one-time fee, quote first. Or move to a host where this class of problem is support's job rather than yours: start on the Basic Linux VPS at $7.99/mo, renewing at $7.99/mo, with full root access, free 30 Gbps DDoS protection and a 30-day money-back guarantee. One caveat, said plainly: Basic suits a small stack, so if you are running eight containers plus a database, Standard at $12.00/mo is the honest fit. That is the Hostaccent trade, from a UK-registered company hosting since 2016 and incorporated in 2018: if your docker container keeps restarting on infrastructure we run, it becomes a ticket, not your weekend.

Frequently Asked Questions About Docker Restart Loops

My docker container keeps restarting after a rebuild. What am I missing?

Rebuilding rarely helps, because the image is seldom the problem. Check whether the container is receiving the same environment it had before: variables, mounted volumes, and file permissions all travel outside the image. Run docker inspect and compare Config.Env and Mounts against a working container. If the rebuild pulled a newer base image, a binary your entrypoint calls may simply no longer exist, which shows up as exit code 127.

How do I stop a Docker restart loop without deleting the container?

Run docker update --restart=no my-container. That changes the policy in place, leaves the container and its filesystem intact, and means the next failure is the last one. You can then read logs, inspect mounted files, and open a shell against the stopped container's image without racing a process that keeps coming back. Restore your intended policy with a second docker update once the underlying fault is fixed and verified.

What does exit code 137 mean in Docker?

It means the process received SIGKILL, which is signal 9 added to the base 128. In most cases the kernel's OOM killer did it because the container passed its memory limit, and docker inspect --format '{{.State.OOMKilled}}' returns true to confirm that. If the flag reads false, something else sent the hard kill: a manual docker kill, a watchdog, or a docker stop whose 10-second grace period expired before your application finished shutting down.

Can a failing health check restart my container by itself?

Docker's built-in health check does not restart containers on its own. It only marks the container unhealthy. What restarts it is something watching that status: Swarm, Kubernetes, an autoheal container, or your own monitoring script. So if a container dies with exit code 143 shortly after being marked unhealthy, find the watcher before you rewrite the application. Nine times out of ten the check thresholds are simply too aggressive for the app's real startup time.

Should I use always or unless-stopped as my restart policy?

Use unless-stopped for almost every long-running service. It behaves like always, with one important difference: if you deliberately stop a container, it stays stopped after the Docker daemon restarts or the server reboots, which is usually what you want at 3am during maintenance. Reserve always for foundational infrastructure that should never be down voluntarily, and use on-failure:3 for batch jobs so a clean exit is not treated as a crash.

Is a restart loop ever the host's fault rather than my app's?

Yes, and it is the case people diagnose last. A full disk on /var/lib/docker, host memory exhaustion picking your container as the OOM victim, or a kernel or storage-driver mismatch after an upgrade all produce loops that look like application bugs from inside the container. Check df -h, docker system df and dmesg -T before rewriting code. On managed plans, our own engineers at Hostaccent will check the host layer for you as part of standard support.

Professional help available

Still not resolved? Let a server specialist take it from here.

Send the symptoms, error output, and what you have already tried. We can work with Hostaccent services or infrastructure hosted with another provider.

  • No hosting transfer required
  • Scope confirmed before paid work
  • No changes before your approval
Request server helpHow server support worksHosted elsewhere? One-time paid support is available after scope and price confirmation.
Reviewed by

Hostaccent Editorial Team

Reviewed for technical accuracy and clarity before publication.

Last updated

Sep 19, 2026

HostAccent Editorial Team publishes practical hosting guides, operations checklists, and SEO-focused tutorials for businesses building international web presence.

Discussion

Have a question or tip about this topic? Share it below — your comment will appear after review.

Your email stays private and is only used for moderation.

Write for the Community

Have a tutorial, tip, or insight to share? Get published on the Hostaccent Blog with your name, bio, and website link.

Become a Contributor

Need a faster setup for this workflow?