You deployed a Docker container, checked the logs, and everything looked fine. But a few minutes later, you notice it’s restarting over and over. The container spins up, runs briefly, crashes, and Docker brings it back—only to repeat the cycle endlessly.

This crash loop is one of the most frustrating issues you’ll encounter with Docker — whether you’re running a self-hosted app stack or a custom service. The good news? It’s almost always diagnosable with a systematic approach.

Here’s a systematic approach to diagnosing container restart loops, finding the root cause, and applying the fix that actually sticks.

What You’ll Need

For this troubleshooting guide, you’ll need:

  • A server with Docker installed (we recommend a Cloud VPS with at least 2GB RAM)
  • SSH access to your server
  • Basic familiarity with the command line

Step 1: Confirm the Restart Loop

First, let’s verify the container is actually in a restart loop. Run:

docker ps -a

Look at the STATUS column. You’ll see something like:

STATUS
Up 3 seconds (Restarting)
Exited (1) 5 seconds ago
Up 2 seconds

The (Restarting) flag or frequent Up/Exited oscillation confirms the loop.

Check how many times it’s restarted:

docker inspect --format='{{.RestartCount}}' container_name

If this number keeps climbing, you’ve confirmed the issue.

Step 2: Check the Container Logs

The logs are your first—and most important—clue. Run:

docker logs container_name

If the container restarts too fast to catch logs, use --tail and --follow:

docker logs --tail 100 --follow container_name

Common log patterns and their meanings:

Log PatternLikely Cause
OOMKilled or Out of memoryContainer exceeds memory limit
permission deniedFile/directory permission issues
connection refusedMissing dependency (database, API)
address already in usePort conflict
command not foundWrong entrypoint or missing binary
exit code 1 (no clear message)Application error—check app logs

Step 3: Common Causes and Fixes

Cause 1: Out of Memory (OOMKilled)

Symptoms: Logs show OOMKilled, or the container dies silently after using too much RAM.

Diagnosis: Check Docker’s memory events:

docker inspect --format='{{.State.OOMKilled}}' container_name

If this returns true, the container hit its memory limit. (This is a common issue with memory-hungry apps — see our Open WebUI memory troubleshooting guide for a real-world example.)

Fix:

  • Increase the memory limit: docker run --memory="2g" ... or update docker-compose.yml
  • For Java apps: Set -Xmx to a value lower than your container limit
  • For Node.js: Set NODE_OPTIONS="--max-old-space-size=1536"
  • Monitor memory usage: Run docker stats while the container runs

Cause 2: Missing Dependencies

Symptoms: Logs show connection refused, host not found, or timeout errors connecting to databases/apis.

The problem: Your container depends on another service (PostgreSQL, Redis, API) that isn’t ready or reachable.

Fix:

  • Use Docker Compose with depends_on: This ensures service start order (though not readiness)
  • Add healthchecks: Wait for dependencies to be ready before starting your app
  • Use wait scripts: Tools like wait-for-it or dockerize can pause startup until a port is available

Example docker-compose.yml with proper dependency handling:

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

Cause 3: Permission Issues

Symptoms: Permission denied errors in logs, especially when writing to mounted volumes.

The problem: The container runs as a non-root user but mounted volumes are owned by root (or vice versa).

Fix:

  • Check ownership: ls -la /path/to/volume on the host
  • Match UID/GID: Ensure the container user’s UID matches the volume owner
  • Use user mapping: docker run --user $(id -u):$(id -g) ...
  • For Docker Compose:
services:
  app:
    user: "1000:1000"
    volumes:
      - ./data:/app/data

Cause 4: Port Conflicts

Symptoms: address already in use or port is already allocated.

The problem: Another process or container is already using the port you’re trying to bind.

Fix:

  • Find what’s using the port: sudo lsof -i :8080 or sudo netstat -tlnp | grep 8080
  • Stop the conflicting service
  • Or change the port mapping: -p 8081:8080

Cause 5: Bad Entrypoint or Command

Symptoms: command not found or immediate exit with code 127.

The problem: The Dockerfile’s ENTRYPOINT or CMD points to a file that doesn’t exist or isn’t executable.

Fix:

  • Verify the path inside the container:
docker run --rm --entrypoint sh image_name -c "ls -la /app/"
  • Check if it’s executable: docker run --rm --entrypoint sh image_name -c "test -x /app/start.sh && echo 'executable'"
  • Override the entrypoint: docker run --entrypoint /bin/sh image_name -c "your_command"

Step 4: Check Your Restart Policy

Docker’s restart policy controls what happens when a container exits. If you set restart: always or restart: unless-stopped, Docker will keep restarting the container—even if it crashes immediately.

Check your current policy:

docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' container_name

While developing or debugging, consider setting restart: "no" or restart: on-failure (with max retries). This prevents endless restart loops while you troubleshoot.

# docker-compose.yml
restart: "no"  # or "on-failure:5"

Step 5: Advanced Debugging

If the basics don’t reveal the issue, try these advanced techniques:

Run the Container Interactively

Bypass the entrypoint and get a shell:

docker run -it --rm --entrypoint /bin/sh image_name

From here, you can manually run commands, check file permissions, test network connectivity, and isolate what’s failing.

Check Exit Codes

The exit code tells you a lot about what went wrong:

Exit CodeMeaning
0Clean exit (app finished intentionally)
1Application error (check logs)
137SIGKILL (often OOMKilled)
139Segmentation fault (app bug)
143SIGTERM (graceful shutdown)

Get the exit code:

docker inspect --format='{{.State.ExitCode}}' container_name

Stream Logs to a Central Location

For production debugging, ship your container logs to a central logging system. Tools like Filebeat, rsyslog, or Loki + Promtail can capture logs even when containers restart too fast to inspect manually.

Centralized logging lets you search historical logs, set up alerts on crash patterns, and correlate container crashes with other system events.

Prevention: Build More Resilient Containers

Once you’ve fixed the immediate issue, here’s how to prevent crash loops in the future (and if you haven’t already, lock down your VPS while you’re at it):

  • Set resource limits: Always define memory and cpu limits in docker-compose.yml
  • Add healthchecks: Docker can restart unhealthy containers before they crash entirely
  • Use init systems: Tools like tini handle zombie processes and signal forwarding
  • Implement graceful shutdown: Handle SIGTERM in your application to clean up resources
  • Test locally: Run docker compose up and monitor for issues before deploying

When to Escalate

If you’ve tried everything and the container still crashes:

  • Check upstream issues: The Docker image itself might have a bug—check GitHub issues or Docker Hub comments
  • Try a different version: Roll back to a previous tag or try :latest if you’re on an older version
  • Rebuild the image: Cache issues can sometimes cause problems—run docker build --no-cache
  • Check host resources: Run free -h and df -h to ensure the host isn’t out of RAM or disk
  • Consider a bigger server: If you’re consistently hitting resource limits, a Dedicated Server gives you bare-metal performance without the shared-resource constraints of a VPS
  • Let the experts handle it: CWH offers Managed Support — our team can diagnose container issues, set up monitoring, and keep your stack running

Summary

Docker container restart loops are frustrating but almost always solvable. The key is a systematic approach:

  • Confirm the loop with docker ps -a
  • Check logs with docker logs
  • Diagnose the root cause (OOM, permissions, dependencies, ports, entrypoint)
  • Fix the underlying issue
  • Adjust restart policies while debugging
  • Implement prevention measures for production

Need a reliable environment to run your Docker containers? Canadian Web Hosting’s Cloud VPS plans with full root access and Canadian data centres. For heavier production workloads, our Dedicated Servers provide bare-metal performance with hardware RAID and ECC RAM. Either way, you get 24/7 expert support and a 99.9% uptime SLA — and if you’d rather not manage servers yourself, add Managed Support and we’ll handle the infrastructure while you focus on your app.