This error occurs when the main process running inside a Docker container (PID 1) terminates unexpectedly, causing the container to stop. The fix typically involves ensuring your application runs in the foreground, handles signals properly, and uses an init process when needed.
In Docker, each container has a main process that runs as PID 1 (Process ID 1). Unlike traditional Linux systems where PID 1 is the init system (systemd, init, etc.), in containers PID 1 is your application itself. When this process exits for any reason, the entire container stops immediately. This error indicates that your container's main process terminated when it was expected to keep running. The container lifecycle is directly tied to this process - there is no background daemon keeping the container alive. If PID 1 exits with code 0, Docker considers it a successful completion. If it exits with any other code, Docker reports it as an unexpected exit. Understanding PID 1 behavior is crucial because it differs from regular processes: PID 1 has special signal handling rules in Linux. By default, signals like SIGTERM are ignored unless explicitly handled, which can cause issues with graceful shutdowns.
First, examine what happened before the container exited. The logs often reveal the root cause:
# View full logs from exited container
docker logs <container_name_or_id>
# View last 50 lines with timestamps
docker logs --tail 50 --timestamps <container_name_or_id>
# Follow logs in real-time (useful during debugging)
docker logs -f <container_name_or_id>Look for error messages, stack traces, or any indication of why the process terminated. If the logs are empty, the process likely crashed before producing any output.
The exit code provides valuable information about why the container stopped:
# Get detailed container state including exit code
docker inspect --format='{{.State.ExitCode}} {{.State.Error}}' <container_id>
# Get full state information
docker inspect --format='{{json .State}}' <container_id> | jqCommon exit codes:
- 0: Process exited successfully (completed its task)
- 1: General application error
- 126: Command cannot execute (permission problem)
- 127: Command not found
- 137: Killed by SIGKILL (often OOM killer, exit code = 128 + 9)
- 139: Segmentation fault (exit code = 128 + 11)
- 143: Killed by SIGTERM (exit code = 128 + 15)
For OOM issues, also check:
docker inspect --format='{{.State.OOMKilled}}' <container_id>One of the most common causes is using shell form instead of exec form for CMD or ENTRYPOINT. Shell form runs your command as a child of /bin/sh, which causes signal handling problems.
Incorrect (shell form):
CMD python app.py
# This runs: /bin/sh -c "python app.py"
# /bin/sh is PID 1, python is a child processCorrect (exec form):
CMD ["python", "app.py"]
# This runs: python app.py directly as PID 1
# Python receives signals directlyThe exec form (JSON array syntax) ensures your application runs directly as PID 1, receiving signals properly. Always use exec form unless you specifically need shell features.
Many applications default to daemon mode, which backgrounds the process and causes the foreground process to exit. You must configure your application to run in the foreground.
Common examples:
# Nginx - use daemon off
CMD ["nginx", "-g", "daemon off;"]
# Apache httpd - use FOREGROUND
CMD ["httpd", "-D", "FOREGROUND"]
# PostgreSQL - runs in foreground by default
CMD ["postgres"]
# Redis - runs in foreground by default
CMD ["redis-server"]
# Node.js - runs in foreground by default
CMD ["node", "server.js"]Avoid these patterns:
# BAD - backgrounds the process
CMD ["sh", "-c", "nginx &"]
# BAD - nohup often causes issues
CMD ["nohup", "python", "app.py"]If using a shell script as your entrypoint, ensure it keeps the main process in the foreground and properly handles the exec handoff:
Problematic entrypoint.sh:
#!/bin/bash
# Setup tasks
echo "Starting application..."
python app.py & # BAD: backgrounds the process
echo "Started" # Script exits here, container stopsFixed entrypoint.sh:
#!/bin/bash
set -e
# Setup tasks (run before main process)
echo "Running initialization..."
# Use exec to replace shell with the main process
# Now python becomes PID 1 and receives signals directly
exec python app.pyThe exec command replaces the shell process with your application, making it PID 1. Without exec, the shell remains PID 1 and your app runs as a child process.
Docker's --init flag adds a tiny init process (tini) as PID 1, which properly forwards signals and reaps zombie processes:
# Run container with init process
docker run --init -d myimage
# In docker-compose.yml
services:
myapp:
image: myimage
init: trueThis is especially useful when:
- Your application doesn't handle SIGTERM properly
- You're running multiple processes in a container
- You see zombie processes accumulating
- docker stop takes the full 10-second timeout
Alternatively, add tini to your Dockerfile:
# Install tini
RUN apt-get update && apt-get install -y tini
# Use tini as entrypoint
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["python", "app.py"]If you cannot use --init, implement signal handling in your application code:
Python example:
import signal
import sys
def handle_sigterm(signum, frame):
print("Received SIGTERM, shutting down gracefully...")
# Cleanup code here
sys.exit(0)
signal.signal(signal.SIGTERM, handle_sigterm)
signal.signal(signal.SIGINT, handle_sigterm)
# Your main application codeNode.js example:
process.on('SIGTERM', () => {
console.log('Received SIGTERM, shutting down gracefully...');
// Cleanup code here
process.exit(0);
});
process.on('SIGINT', () => {
console.log('Received SIGINT, shutting down gracefully...');
process.exit(0);
});Bash script example:
#!/bin/bash
trap 'echo "Caught SIGTERM"; exit 0' TERM
trap 'echo "Caught SIGINT"; exit 0' INT
# Your main logic here
while true; do
# Do work
sleep 1
doneTo investigate what's happening inside the container, run it interactively:
# Override entrypoint to get a shell
docker run -it --entrypoint /bin/sh myimage
# Or /bin/bash if available
docker run -it --entrypoint /bin/bash myimage
# Once inside, manually run your application
./entrypoint.sh
# or
python app.pyThis lets you:
- Check if required files exist
- Verify environment variables are set correctly
- Test the application startup manually
- See error messages that might not appear in logs
For containers that exit immediately:
# Keep container running with tail
docker run -d myimage tail -f /dev/null
# Then exec into it
docker exec -it <container_id> /bin/shIf your container is being killed by the OOM (Out of Memory) killer, you'll see exit code 137:
# Check if container was OOM killed
docker inspect --format='{{.State.OOMKilled}}' <container_id>
# Check kernel logs for OOM messages (on the host)
dmesg | grep -i "killed process"Solutions:
1. Increase memory limit:
docker run -m 2g myimage
# docker-compose.yml
services:
myapp:
image: myimage
deploy:
resources:
limits:
memory: 2G2. Optimize application memory usage
3. Add swap (not recommended for production):
docker run -m 1g --memory-swap 2g myimageUnderstanding PID 1 in Linux vs Containers
In traditional Linux, PID 1 is the init system (systemd, SysV init) which has special kernel protections - it cannot be killed by signals unless it explicitly handles them. In containers, your application becomes PID 1, inheriting these protections. This means SIGTERM and SIGINT are ignored by default unless your application registers signal handlers.
The exec vs shell form behavior
When using shell form CMD command arg1 arg2, Docker actually runs /bin/sh -c "command arg1 arg2". This means:
1. /bin/sh is PID 1
2. Your command runs as a child process
3. Signals sent to the container go to /bin/sh first
4. Shell may not forward signals to child processes
5. When the shell exits, your process becomes orphaned
Multi-process containers
If you need multiple processes in a container (not recommended but sometimes necessary), use a proper process supervisor:
# Using supervisord
COPY supervisord.conf /etc/supervisor/conf.d/
CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
# Using s6-overlay (Alpine-friendly)
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.1.5.0/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ENTRYPOINT ["/init"]Debugging with strace
For mysterious exits, trace system calls:
# Install strace in container, then:
docker run -it --cap-add SYS_PTRACE myimage strace -f -e trace=process python app.pyHealth checks as early warning
Use Docker health checks to detect issues before complete failure:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1unable to configure the Docker daemon with file /etc/docker/daemon.json
How to fix 'unable to configure the Docker daemon with file daemon.json' in Docker
docker: Error response from daemon: OCI runtime create failed: container_linux.go: starting container process caused: exec: "/docker-entrypoint.sh": stat /docker-entrypoint.sh: no such file or directory
How to fix 'exec: entrypoint.sh: no such file or directory' in Docker
image operating system "linux" cannot be used on this platform
How to fix 'image operating system linux cannot be used on this platform' in Docker
dockerfile parse error line 5: unknown instruction: RRUN
How to fix 'unknown instruction' Dockerfile parse error in Docker
manifest unknown: manifest unknown
How to fix 'manifest unknown' in Docker