Docker Container Exits Immediately: How to Fix It (Complete Troubleshooting Guide)

Docker Container Exits Immediately: How to Fix It (Complete Troubleshooting Guide)

Picture this: you have just spent the last hour crafting the perfect Dockerfile. You run the build command without any errors. Feeling accomplished, you execute the run command, expecting your microservice to spring to life. Instead, you are instantly dumped back into your terminal prompt. You check your running containers, and there is nothing there. Your container lived for a fraction of a second and vanished into the digital ether.

If you are experiencing this, you are not alone. Searching for “docker container exits immediately how to fix” is a rite of passage for every developer working with containerized applications. As someone who has built and maintained container architectures for years, I have seen firsthand how a tiny misconfiguration can bring an entire deployment pipeline to a halt.

In this comprehensive troubleshooting guide, we are going to dissect exactly why containers exit unexpectedly. We will walk through root cause analysis, step-by-step solutions starting from the most common pitfalls to the more elusive edge cases, and arm you with the knowledge to prevent this from happening in the future.

Understanding the Root Cause: Why Do Containers Exit?

To fix the problem, we first need to understand the philosophy of Docker. Unlike traditional virtual machines that boot up a full operating system and wait for you to log in, Docker containers are designed to be ephemeral.

A Docker container lives exactly as long as its primary process—often referred to as the foreground process or PID 1 (Process ID 1).

If the command specified in your Dockerfile (via CMD or ENTRYPOINT) completes its task or crashes, PID 1 terminates. The moment PID 1 stops, the Docker engine assumes the container’s job is done, and it shuts it down.

  • If your app runs a quick script and finishes, the container exits.
  • If your app encounters an unhandled exception and crashes, the container exits.
  • If your app is designed to run in the background (like Nginx or Apache default configurations), the foreground process terminates, and the container exits.

With this foundational knowledge, let’s roll up our sleeves and start fixing the issue.

Step-by-Step Troubleshooting Guide

Step 1: Read the Room (Check the Container Logs)

When developers ask me how to approach a suddenly exiting container, my first question is always: “What do the logs say?”

You cannot fix a problem if you do not know what it is. Docker captures the standard output (stdout) and standard error (stderr) of PID 1.

First, find your stopped container’s ID or name:

docker ps -a

Look for the container with a status of “Exited”. Once you have the container ID, query its logs:

docker logs <container_id_or_name>

If you are running a modern setup with Docker Compose, the command is even easier:

docker compose logs <service_name>

What to look for:
Look for stack traces, syntax errors, or connection timeouts. If you see a specific application error (like a Python ModuleNotFoundError or a Node.js ECONNREFUSED), you have your answer. Fix the code or the configuration, rebuild the image, and run it again.

If the logs are completely empty, it means your application didn’t even get a chance to write to standard output before it exited. This brings us to the next step.

Step 2: Decode the Exit Status

If the logs are empty or unhelpful, Docker’s exit codes act as a black box recorder, telling you exactly how the process met its end. You can find the exit code by inspecting the container:

docker inspect <container_id> --format='{{.State.ExitCode}}'

Here is a breakdown of the most common exit codes you will encounter in 2026’s container landscape:

Exit Code 0: The “Graceful” Exit

An exit code of 0 means the process completed successfully. This is a great sign for a script that runs a backup and finishes, but a terrible sign for a web server.
* The Fix: Your container lacks a foreground process. See Step 3 below.

Exit Code 1: Application Crash

Exit code 1 indicates a general error or exception thrown by the application itself.
* The Fix: Revisit Step 1. Your application threw a fatal error (like an uncaught exception or missing dependency). You must resolve the application-level bug.

Exit Code 137: Out of Memory (OOM) Killed

If you see 137, it means your container was killed by the host operating system because it exceeded its allocated memory limits.
* The Fix: See Step 5 for memory profiling.

Exit Code 139: Segmentation Fault

A 139 means your application tried to access a restricted memory location. This is common in lower-level languages like C or C++, or when using native node modules.
* The Fix: Ensure your base image has the correct OS-level libraries and compilers installed.

Step 3: The Missing Foreground Process

This is arguably the most common reason developers search for “docker container exits immediately how to fix”. It happens when the application forks itself to the background during the startup sequence.

A classic example is running Nginx.

The Wrong Way:

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nginx
CMD ["nginx"]

If you run this, Nginx starts, forks worker processes, and the master process detaches from the foreground. Docker sees PID 1 terminate, assumes Nginx is done, and shuts down the container.

The Fix:
You must instruct the application to stay in the foreground.

For Nginx, you append the daemon off; flag:

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nginx
CMD ["nginx", "-g", "daemon off;"]

The “Hack” for Quick Debugging:
If you are dealing with a stubborn image and just need to keep it alive to investigate, override the CMD to run a never-ending process. I use this constantly during debugging sessions:

# Keeps the container alive indefinitely so you can shell into it
docker run -d my-stubborn-image tail -f /dev/null

# Or using sleep
docker run -d my-stubborn-image sleep infinity

Once the container is running, you can exec into it and investigate manually:

docker exec -it <container_id> /bin/bash

Step 4: Entrypoint Script Failures

In modern Dockerfiles, developers often use an entrypoint.sh script to handle dynamic configuration at runtime (like replacing environment variables in config files before starting the app).

However, if the script is missing the proper execution permissions or contains a syntax error, the container will exit immediately.

The Problematic Script (entrypoint.sh):

#!/bin/bash
# Configures database URL
sed -i "s/DB_URL_PLACEHOLDER/$DB_URL/g" /etc/app/config.yaml

# Starts the application
python main.py

If this script is copied into the image but not made executable, Docker will throw an “Permission denied” error (Exit Code 126) and stop.

The Fix:
Always ensure you grant execution permissions in your Dockerfile after copying the script:

WORKDIR /app
COPY entrypoint.sh .
RUN chmod +x entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]

Furthermore, a common best practice for entrypoint scripts is using the exec command for the final process. The exec command replaces the shell script with the application, ensuring your app becomes PID 1 and properly receives system signals (like SIGTERM for graceful shutdowns).

#!/bin/bash
set -e

sed -i "s/DB_URL_PLACEHOLDER/$DB_URL/g" /etc/app/config.yaml

# Use exec to hand over PID 1 to the Python process
exec python main.py

Step 5: Out of Memory (OOM) Crashes

Sometimes your container exits because it is greedy. Modern orchestration tools like Kubernetes or Docker Swarm impose strict resource limits. If your Java JVM, Node.js engine, or Python data processing script tries to consume more RAM than allocated, the Linux kernel’s OOM killer steps in and forcefully terminates it, resulting in Exit Code 137.

The Fix:
First, try running the container without memory limits to see if it survives:

docker run --memory="4g" my-java-app

If it survives with more memory, you need to optimize your application’s memory footprint.
* Java: Ensure you are using modern JVM features (like Java 21+ ZGC or Shenandoah) and capping the heap size (-Xmx). In Docker, JVMs older than Java 8u191 could not read container limits and would try to use the host’s total memory, causing severe OOM issues. Always use up-to-date base images.
* Node.js: If using Node.js v20 or newer, use the --max-old-space-size flag to ensure V8 stays within Docker limits.
* Python: Use memory profilers (memory_profiler) to ensure your data structures aren’t leaking.

Step 6: File System and Permission Edge Cases

As we move into the more obscure edge cases, file system permissions frequently cause silent, immediate exits.

This often happens when you mount a host volume into the container, and the internal application expects to run as a non-root user. The container starts, attempts to write to the mounted directory, realizes it doesn’t have chown permissions on the host volume, and crashes.

The Fix:
Ensure your Dockerfile properly creates and switches to a non-root user, and that the mounted volumes have the correct ownership.

# Create a non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser

# Create a directory and give the user ownership
RUN mkdir /app/data && chown -R appuser:appuser /app/data

# Copy application and set ownership
COPY --chown=appuser:appuser . /app

# Switch to the non-root user
USER appuser

CMD ["python", "app.py"]

Step 7: Dependency Init Order (Networking and Volumes)

In a microservices architecture, your container might exit because it expects a database to be available at startup, but the Docker network hasn’t fully initialized the connection, or the target container isn’t ready yet.

Your app throws an unhandled ConnectionRefused error, resulting in an Exit Code 1.

The Fix:
Do not rely solely on tools like dockerize or wait-for-it.sh to solve dependency issues. While they are great for pinging ports, the best approach is to make your application resilient.

Write your application code so that database connection pools handle retries with exponential backoff. Here is a conceptual Python example:

“`python
import time
import psycopg2
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=4, max=10))
def get_db_connection():
return psycopg2.connect(os.environ[“DATABASE_URL”])

try:
conn = get_db_connection()
except Exception as e:
print(“Failed to connect to database after multiple retries”)
sys.exit(1)

Leave a Reply

Your email address will not be published. Required fields are marked *