You run docker run myimage and… nothing. The container starts, exits immediately, and you’re left staring at a silent terminal. Sound familiar?

This is one of the most common Docker frustrations, especially for beginners. Let’s fix it.

Understanding Why Containers Exit

A Docker container runs as long as its main process (PID 1) is running. When that process exits, the container exits. This is by design — containers aren’t VMs that sit around waiting for something to happen.

The exit code tells you what happened:

  • Exit code 0: Process completed successfully (this is often the surprise)
  • Exit code 1: General error
  • Exit code 137: Container was killed (OOM or docker kill)
  • Exit code 139: Segmentation fault
  • Exit code 143: Graceful termination (SIGTERM)

The Most Common Causes

1. Your Command Completes Immediately

This is the #1 cause. Your Dockerfile or run command executes something that finishes instantly.

The problem:

1
2
3
FROM ubuntu
RUN apt-get update && apt-get install -y nginx
CMD ["nginx"]

Nginx by default runs in the background (daemonizes) and exits. The container sees the process end and shuts down.

The fix:

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

The -g "daemon off;" flag keeps nginx in the foreground.

2. Shell Form vs Exec Form Confusion

These look similar but behave differently:

1
2
3
4
5
# Shell form - runs in /bin/sh -c
CMD npm start

# Exec form - runs directly
CMD ["npm", "start"]

Shell form wraps your command in a shell, which can cause signal handling issues and unexpected exits. Always prefer exec form unless you specifically need shell features.

3. Your Script Has No Loop

If your entrypoint is a script that runs and completes, the container exits.

The problem:

1
2
3
4
#!/bin/bash
echo "Starting app..."
export CONFIG_LOADED=true
# Script ends, container exits

The fix:

1
2
3
4
#!/bin/bash
echo "Starting app..."
export CONFIG_LOADED=true
exec "$@"  # Hand off to the CMD

Or if you need the container to stay alive:

1
2
3
4
#!/bin/bash
echo "Starting app..."
# Keep container running
tail -f /dev/null

4. Application Crash on Startup

Your app might be crashing before it even gets going. Check the logs:

1
2
3
4
5
# See why it died
docker logs <container_id>

# Or run interactively to debug
docker run -it myimage /bin/bash

Common culprits:

  • Missing environment variables
  • Config file not found
  • Port already in use
  • Dependency not installed

5. The Entrypoint/CMD Interaction

When both ENTRYPOINT and CMD exist, CMD becomes arguments to ENTRYPOINT:

1
2
3
ENTRYPOINT ["python"]
CMD ["app.py"]
# Actually runs: python app.py

If your ENTRYPOINT doesn’t handle arguments properly, it might just exit.

Debugging Checklist

Step 1: Check the exit code

1
2
docker ps -a
# Look at the STATUS column for exit code

Step 2: Check the logs

1
docker logs <container_id>

Step 3: Run interactively

1
2
docker run -it myimage /bin/sh
# Now poke around inside the container

Step 4: Override the entrypoint

1
2
docker run -it --entrypoint /bin/sh myimage
# Bypasses your entrypoint to debug

Step 5: Keep it alive temporarily

1
2
3
docker run -d myimage tail -f /dev/null
# Forces container to stay running so you can exec into it
docker exec -it <container_id> /bin/bash

Quick Reference: Common Services

ServiceForeground Flag
nginxnginx -g "daemon off;"
Apacheapache2ctl -D FOREGROUND
PHP-FPMphp-fpm -F
Redisredis-server (foreground by default)
MySQLmysqld (foreground by default)

The Pattern That Always Works

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
FROM your-base-image

# Install dependencies
RUN apt-get update && apt-get install -y whatever

# Copy your app
COPY . /app
WORKDIR /app

# Run something that stays in foreground
CMD ["./your-app", "--foreground"]

The key insight: your main process must stay in the foreground and not exit. If it backgrounds itself, pipes to another process, or completes its work, the container stops.

Still Stuck?

If your container still exits immediately after trying these fixes, the issue is almost certainly in your application code — it’s crashing before producing any useful output. Add some logging at the very start of your entrypoint to confirm it’s even being reached:

1
2
3
#!/bin/bash
echo "Entrypoint started at $(date)" >> /var/log/startup.log
# rest of your script

Then check if that file exists after the container exits.

Containers aren’t magic — they’re just processes with fancy isolation. Once you internalize “container = foreground process,” these issues become much easier to debug.