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 Pattern | Likely Cause |
|---|---|
OOMKilled or Out of memory | Container exceeds memory limit |
permission denied | File/directory permission issues |
connection refused | Missing dependency (database, API) |
address already in use | Port conflict |
command not found | Wrong 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
-Xmxto a value lower than your container limit - For Node.js: Set
NODE_OPTIONS="--max-old-space-size=1536" - Monitor memory usage: Run
docker statswhile 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-itordockerizecan 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/volumeon 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 :8080orsudo 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 Code | Meaning |
|---|---|
| 0 | Clean exit (app finished intentionally) |
| 1 | Application error (check logs) |
| 137 | SIGKILL (often OOMKilled) |
| 139 | Segmentation fault (app bug) |
| 143 | SIGTERM (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
memoryandcpulimits in docker-compose.yml - Add healthchecks: Docker can restart unhealthy containers before they crash entirely
- Use init systems: Tools like
tinihandle zombie processes and signal forwarding - Implement graceful shutdown: Handle SIGTERM in your application to clean up resources
- Test locally: Run
docker compose upand 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
:latestif 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 -handdf -hto 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.
Be First to Comment